Redux-Form: Not able to change value of input elements - redux-form

I've been trying to implement a form in MapsAddrForm.jsx using Redux-Form and I can't seem to change the value of my input element. When the page loads, the input element does not respond to keyboard input, and when the form field submits, it returns an empty object to the parent component DistrictFinder. Beyond these two files, I've also added form:formReducer as an argument to combineReducers much like the simple example in the Redux-Form tutorials. Is there any way to restore the ability for the DistrictFinder to receive data objects from the address form? For reference, I'm using React 15.1.0, React-redux 4.4.5, ES6, and Redux-Form 5.3.1, all compiled using Webpack.
MapsAddrForm.jsx
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {reduxForm} from 'redux-form';
class MapsAddrForm extends Component {
constructor(props) {
super(props);
}
render() {
const {fields: {address,address2}, handleSubmit} = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<input type="text" placeholder="Your address" {...address}/>
</div>
<button type="submit">Submit</button>
</form>
);
}
}
export default reduxForm({
form: 'addressForm',
fields: ['address']
})(MapsAddrForm);
DistrictFinder.jsx
import React, { Component, PropTypes } from 'react'
import MapsAddrForm from './MapsAddrForm.jsx'
import { connect } from 'react-redux'
import { changeAddress } from '../actions/index.jsx'
class DistrictFinder extends Component {
constructor(props) {
super(props);
this.handleAddrSubmit = this.handleAddrSubmit.bind(this);
}
handleAddrSubmit(data) {
console.log("Address received: " + JSON.stringify(data));
}
render() {
const {address, district} = this.props
return (
<div class="GMaps">
<h1>Find your district!</h1>
<MapsAddrForm onSubmit={this.handleAddrSubmit} />
<p>My district number is: {district}</p>
</div>
);
}
}
DistrictFinder.propTypes = {
district: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
const { district } = state.infoChange;
return {
district
};
};
export default connect(mapStateToProps)(DistrictFinder);

I ran into this as well on redux-form#6.2.0
After looking at the source for one of the examples, I noticed the call to combineReducers has an explicit key "form" to the object argument. When I added this explicit key, the fields became editable.
// Correct
const reducer = combineReducers({
form: reduxFormReducer // mounted under "form"
})
If you have an existing app, like I do, you likely have this style es6 syntax from the various redux starters.
// Incorrect: results in the witnessed bad behavior
const reducer = combineReducers({
reduxFormReducer
})
See the combineReducers call at https://github.com/erikras/redux-form/blob/master/examples/simple/src/index.js#L10
It'd be interesting to see if this could be a constant that could be passed in and leveraged by the lib. "form" feels like a easily corruptible "namespace".

Related

React - Redux Form - handleSubmit

Looking for some insight with the Redux Form handleSubmit function. I am following along to Set Griders course from Udemy. Everything is running smoothly until here. Other error/bugs I was able to research and solve them myself. I am really struggling to find a solution here.
singin.js
import React, { Component } from 'react'
import { reduxForm } from 'redux-form'
import { Checkbox, Form, FormField, Button, Grid, GridColumn, Input } from 'semantic-ui-react'
class SignIn extends Component {
onFormSubmitHandler({ email, password }) {
console.log('Values', email, password)
}
render() {
// this.props are brought to us by reduxForm. Allowing to grab the field inputs and submit action
const { handleSubmit, fields: { email, password } } = this.props
return (
<Grid centered columns={2}>
<GridColumn>
<Form onSubmit={handleSubmit(this.onFormSubmitHandler.bind(this))}>
<FormField>
<label>Email:</label>
<Input placeholder='Email' {...email} />
</FormField>
<FormField>
<label>Password:</label>
<Input placeholder='Password' {...password} />
</FormField>
<FormField>
<Checkbox label='I agree to the Terms and Conditions' />
</FormField>
<Button action='submit'>Sign In</Button>
</Form>
</GridColumn>
</Grid>
)
}
}
export default reduxForm({
form: 'signin',
fields: ['email', 'password']
})(SignIn)
rootReducer
import { combineReducers } from 'redux'
import SetsReducer from './SetsReducer'
import { reducer as form } from 'redux-form'
const rootReducer = combineReducers({
sets: SetsReducer,
form
})
export default rootReducer
The console.log inside onFormSubmitHandler logs: 'Values undefined undefined'
Edit: Project on GitHub
I solved the issue. The fix involved importing 'Field' from redux-form and replacing the input element with elements. Passing {Input} as the component to render

How to retrieve an input form value with redux-form and formValueSelector

I am trying to retrieve a single input value and log it. This is an edit form with existing name value prop.
I am not setting state 'name' with field input's value for some reason. I am not sure how to structure the connect part which I think is my problem. In particular, I am not clear on how to write mapStateToProps to include both my non-form state and form state.
partial scaled down code:
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { connect } from 'react-redux';
import { updateStable } from '../../actions/index';
const selector = formValueSelector('myEditForm');
class EditStuff extends Component {
constructor(props) {
super(props);
this.state = {
name: this.props.name
};
}
componentDidMount() {
this.props.initialize({
name: this.props.name || ''
});
}
componentDidUpdate() {
// this.state.name is not getting set from input value
this.props.updateLocalActiveStuffData(this.state.name);
}
render() {
const { handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label="Name"
name="name"
class="name"
type="text"
component={renderField}
/>
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
displayEditForm: state.displayEditForm, //my own non-form related state
name: selector(state, 'name') //form input 'name'
};
}
export default connect(mapStateToProps, { updateStuff })(
reduxForm({
form: 'myEditForm'
})(EditStuff)
);
I think if you can't retrieve the value it's because the name of your form in reduxForm and the formValueSelector name are different 'StableEditForm' != 'myEditForm'
You have more info on selectors here
If you want to initialise your form with values, you should set it from your state in mapStateToProps with the initialValues props, something like this:
function mapStateToProps(state) {
return {
initialValues: state.displayEditForm, // value of your form
};
}
A great exemple here
I hope this can help you

redux-form not populating FieldArray with InitialValues object

I'm working with react-redux-form 7.03 and attempting to populate fields from a database. In the interests of simplifying to determine where my issue is, I've created a static array of 1 object to try to get information to display.
I'm fairly new to React and Redux, and I've been fighting this for three days now. I've been using the examples from https://codesandbox.io/s/qDjw3zGG and the redux-form examples with no luck.
Basic issue is that in the props, I'm seeing initialValue:xmlConfigDataSet with the correct information in the object (array with 1 object correctly filled in). However that is not being passed to the fields property to RenderRows. Debugging fields shows a length of 0 and it just passes over the map() and never renders.
import React, {Component} from 'react'
import {connect} from 'react-redux'
import { Field, FieldArray, reduxForm } from 'redux-form'
class XMLMatchUpForm extends Component {
constructor(props){
super(props);
this.state = {}
}
render(){
return (
<FieldArray name="xmlConfigDataSet" component={RenderRows} />
)
};
const RenderRows = ({fields, meta}) => (
<div>
{fields.map((field, index) => {
const value = fields.get(index);
return <RenderStaticField name={field} value = {value} />
})}
</div>
)
const RenderStaticField = (props) => {
<Field name={`${props.name}.MW_MD_VALUE`} component="input" />
}
XMLMatchUpForm = reduxForm({
form: "XMLMatchUpForm"
}) (XMLMatchUpForm);
function mapStateToProps(state) {
return {
initialValues: {
xmlConfigDataSet:[{"MW_XS_ID":1314,"MW_MD_ID":null,"MW_XDM_VALUE":"Silver Spring","MW_XS_TAG_NAME":"receivercity"}]
}
};
}
export default connect(mapStateToProps, {fetchXMLConfig}) (XMLMatchUpForm);
(ignore the {fetchXMLConfig} in the example, that is actually the action it is caling to get the xmlConfigDataSet, as I said above, I'm trying to simplify this to determine where the issue is.
Any ideas, or pointers to clear examples are welcome.

Idiomatic way of making a thunk action available to components?

I have written the following thunk action creator which is used to make a request to an api.
If my understanding is correct, the thunk action creator will be handled by middleware and have access to the store dispatch method.
What is the idiomatic way of making this thunk action creator available to a react component?
The best method I can think of is just importing the thunk action creator directly.
export function fetchMovie(title) {
return (dispatch) => {
dispatch(requestMovie(title));
const url = `http://www.omdbapi.com/?t=${title}&y=&plot=short&r=json`
return axios.get(url)
.then(response => {
dispatch(receiveMovie(title, response.data))
})
.catch(err => dispatch(requestMovieErr(title, err)))
}
}
Yes your assumption is correct. The most common way would be to import individual action functions into your components as needed, such as:
import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
// step 1: import action 'fetchMovie'
import { fetchMovie } from './actions/whateverYourFileIsCalled';
class SomeComponent extends Component {
render() {
return (
<div>
{/*
step 3: use the 'fetchMovie' action
that is now part of the component's props
wherever we'd like
*/}
<button onClick={this.props.fetchMovie.bind(null, 'Jurassic Park')}>
Click here for dinosaurs
</button>
</div>
);
}
}
SomeComponent.propTypes = {
fetchMovie: PropTypes.func.isRequired
};
export default connect(
{},
{
// step 2:
// connect the 'fetchMovie' action to this component's props using redux helper
fetchMovie
}
)(SomeComponent);

BASE64 to image angular 2

I'm trying to show an image get from a remote server with angular 2.
In my component I have an object that is an "university_info" object that is my model.
export class myClass
{
university_info : university_info;
}
myFunction()
{
this.university_info = new university_info(responseFromServer[image])
}
export class university_info
{
imageBase64 : string
constructor(image : string)
{
this.imageBase64 = image
}
}
All is working fine. I get base64 image but when I try to show it in HTML in this way :
<img [src]="'data:image/jpg;base64,'+university_info.imageBase64" />
That's is what I get :
I feel like this thread lacks concrete examples which made me have some difficulties:
Import DomSanitizer:
import { DomSanitizer } from '#angular/platform-browser';
define in constructor:
constructor(private _sanitizer: DomSanitizer) { }
Sanitize the Base64 string you want to pass as your image source (use trustResourceUrl):
this.imagePath = this._sanitizer.bypassSecurityTrustResourceUrl('data:image/jpg;base64,'
+ toReturnImage.base64string);
Bind to html:
<img [src]="imagePath">
Solution: Just use 'data:image/jpg;base64' into your image tag like this
<img src="{{'data:image/jpg;base64,' + imagePath}}" />
You can try to use _sanitizer.bypassSecurityTrustUrl to tell angular src value is safe. Take a look at this class from angular:
class DomSanitizationService {
sanitize(context: SecurityContext, value: any) : string
bypassSecurityTrustHtml(value: string) : SafeHtml
bypassSecurityTrustStyle(value: string) : SafeStyle
bypassSecurityTrustScript(value: string) : SafeScript
bypassSecurityTrustUrl(value: string) : SafeUrl
bypassSecurityTrustResourceUrl(value: string) : SafeResourceUrl
}
and be low an example for safe html:
export class AppComponent {
private _htmlProperty: string = '<input type="text" name="name">';
public get htmlProperty() : SafeHtml {
return this._sanitizer.bypassSecurityTrustHtml(this._htmlProperty);
}
constructor(private _sanitizer: DomSanitizationService){}
}
Please find enclosed a proper example of how to upload an image in Base64 in Angular 2/4 and also its display. the actual base64 string is dumped into the debugger console and of course can be stored in database etc.
import { Component, OnInit } from '#angular/core';
// Base 64 IMage display issues with unsafe image
import { DomSanitizer } from '#angular/platform-browser';
#Component({
selector: 'app-test',
template: `
<h1>Test 001 </h1>
<div class="form-group">
<label>Image</label>
<input type="file" class="form-control" accept="application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint,
text/plain, application/pdf, image/*" (change)="changeListener($event)">
</div>
<img *ngIf="base64Image" [src]="domSanitizer.bypassSecurityTrustUrl(base64Image)" />
`,
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
private base64Image: string;
constructor(private domSanitizer: DomSanitizer) { }
ngOnInit() {
}
changeListener($event): void {
this.readThis($event.target);
}
readThis(inputValue: any): void {
var file: File = inputValue.files[0];
var myReader: FileReader = new FileReader();
myReader.onloadend = (e) => {
this.base64Image = myReader.result;
console.log(this.base64Image);
}
myReader.readAsDataURL(file);
}
}
You have to make sure that university_info.imageBase64 is a string type then you code will work.
DEMO : http://plnkr.co/edit/pI35tx9gXZFO1sXj9Obm?p=preview
import {Component,ViewChild,Renderer,ElementRef,ContentChildren} from '#angular/core';
#Component({
selector: 'my-app',
template: `
<img [src]="'data:image/jpg;base64,'+imagePath"/>
`
})
export class App {
imagePath:string="iVBORw0KG...";
}
I would like to propose an alternative solution that builds on the one provided by #gatapia.
The proposed solution is to use the #ViewChild decorator tag (see docs here https://angular.io/docs/ts/latest/api/core/index/ViewChild-decorator.html), to retrieve the element reference within the component, and set the value directly as illustrated in the code snippet below. Important to note that the element being referenced via the ViewChild should be bound with to a local variable using the #, as illustrated in the code snipped below.
Also as the ElementRef docs explains, using the ElementRef directly still exposes risk of XSS also present when using DomSanitizer.
#Component({
template: `
<div>
<img #imgRef> // Note that the #imgRef reference is required to be identified by Angular
</div>
`,
})
export class MyComponent implements OnInit {
src:string;
#ViewChild('imgRef') img:ElementRef;
constructor() {
// In your case, this will be resolved from the server
this.src = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAyNTAgMjUwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNTAgMjUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDojREQwMDMxO30NCgkuc3Qxe2ZpbGw6I0MzMDAyRjt9DQoJLnN0MntmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTI1LDMwIDEyNSwzMCAxMjUsMzAgMzEuOSw2My4yIDQ2LjEsMTg2LjMgMTI1LDIzMCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAJIi8+DQoJPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIxMjUsMzAgMTI1LDUyLjIgMTI1LDUyLjEgMTI1LDE1My40IDEyNSwxNTMuNCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAxMjUsMzAgCSIvPg0KCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMjUsNTIuMUw2Ni44LDE4Mi42aDBoMjEuN2gwbDExLjctMjkuMmg0OS40bDExLjcsMjkuMmgwaDIxLjdoMEwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMQ0KCQlMMTI1LDUyLjF6IE0xNDIsMTM1LjRIMTA4bDE3LTQwLjlMMTQyLDEzNS40eiIvPg0KPC9nPg0KPC9zdmc+DQo=';
}
ngOnInit() {
// Sets the value of the element
this.img.nativeElement.src = this.src;
}
}
The following plunkr provides a working code snippet of the above https://plnkr.co/edit/JXf25Pv8LqrFLhrw2Eum?p=preview
This question gets high google ranking so I thought I'd put my findings here. Using data binding to set the [src] property of an image can be problematic especially on some older mobile devices. So if you have performance issues with the sanitizer+binding approach you will have to set the src property using the DOM directly:
constructor(private el: ElementRef) {}
...
public imageChanged(base64: string) {
const im: HTMLImageElement = this.el.nativeElement.querySelector('#imgid');
im.src = data;
}
This may be ugly but its lightning fast.

Resources