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.
Related
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
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
In my project we are building a form with React and Redux-Form. We have a single information that is composed by the value of two inputs. But the values of each input is combined and validated together.
The first implementation try was by connecting each component with Field. It let us update the state properly but we couldn't validate all the values together with the validate prop.
The second Try was using Fieldscomponent but it does not have a validate prop. We thought in create a Pull Request for it but the API for it isn't clear yet, since what we want to validate the combination of the two values and the behavior of the Fields props (such as parse and format) is different, executing the function for each input inside Fields component separately.
I know it is possible to create a component and use Field to connect with the application state, but I didn't want to manage things as the touched prop, or the callbacks to update the state, or other things that I even have noticed, since Redux-Form has all of it done.
The fact is that I end up with an implementation but it didn't looked very elegant. I'd like you to take a look at the implementation and give your opinion, sugest other solutions and even if this solution is not implemented in Redux-Form yet we could maybe open a pull request for that.
Here is an example implementation
Simple form container
import SimpleForm from 'app/simpleForm/components/simpleForm'
import { reduxForm } from 'redux-form'
export default reduxForm({
form: 'simpleForm'
})(SimpleForm)
Simple form component
import React from 'react'
import { Field } from 'redux-form'
import MultiInputText from 'app/simpleForm/components/multiInputText'
const onSubmit = values => alert(JSON.stringify(values))
const validateAddress = (value) => {
if (!value) return 'Address is empty'
if (!value.street) return 'Street is empty'
if (!value.number) return 'Number is empty'
return null
}
const SimpleForm = ({ handleSubmit }) => {
return (
<form onSubmit={ handleSubmit(onSubmit) }>
<Field label="Home Address" name="home" component={MultiInputText} type="text" validate={validateAddress}/>
<Field label="Work Address" name="work" component={MultiInputText} type="text" validate={validateAddress}/>
<button type="submit">Submit</button>
</form>
)
}
export default SimpleForm
MultiInputText component
import React from 'react'
import { Fields, FormSection } from 'redux-form'
const renderAddreessInputs = ({ street, number, error }) => (<div>
<input {...street.input} type="text" />
<input {...number.input} type="text" />
{ street.meta.touched && number.meta.touched && error && <span className="error">{error}</span> }
</div>)
const MultiInputText = (props) => {
const { input: { name }, label, meta: { error }} = props
const names = [
'street',
'number'
]
return (<div>
<label htmlFor={name}>{label}</label>
<FormSection name={name}>
<Fields names={names} component={renderAddreessInputs} error={error}/>
</FormSection>
</div>)
}
export default MultiInputText
I see two options:
1) Use record-level validation.
reduxForm({
form: 'addressForm',
validate: values => {
const errors = {}
if(!home) {
errors.home = 'Address is empty'
}
// etc, etc. Could reuse same code for home and work
return errors
}
})
2) Create a single input that handles a complex value.
<Field name="home" component="AddressInput"/>
...
const AddressInput = ({ input, meta }) =>
<div>
<input
name={`${input.name}.street`}
value={(input.value && input.value.street) || ''}
onChange={event => input.onChange({
...input.value,
street: event.target.value
})}/>
...other inputs here...
</div>
That's total pseudocode, but I hope it gets the point across: a single input can edit a whole object structure.
Personally, I'd choose Option 1, but I prefer record-level validation over field-level validation in general. The nice thing about Option 2 is that a single AddressInput could be reused across the application. The downside is that you don't get specific field-level focus/blur/dirty/pristine state.
Hope that helps...?
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);
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".