Redux-Form les valeurs initiales de
J'essaie de remplir le formulaire de profil avec les données de L'API. Malheureusement, redux-form ne veut pas coopérer avec moi dans ce cas. Pour une raison quelconque, les champs restent vides quoi que je fasse.
Définir les valeurs fixes au lieu des valeurs transmises par le réducteur fonctionne bien pour une raison quelconque.
Peut-être que c'est parce que j'utilise Redux-promise pour les appels D'API dans les créateurs d'action? Comment puis-je vivre avec et me débarrasser de cela. Voici mon composant de formulaire.
import React, { Component } from 'react';
import { reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';
import { fetchRoleList, fetchUserData } from '../actions';
class UserEdit extends Component {
componentWillMount() {
this.props.fetchRoleList();
this.props.fetchUserData();
}
handleEditProfileFormSubmit(formProps) {
console.log(formProps);
}
getRoleOptions(selected_id) {
if (!this.props.profile) {
return <option>No data</option>;
}
return this.props.profile.roles.map(role => {
return <option key={role.role_id} value={role.role_id}>{role.name}</option>;
});
}
renderField(props) {
const { input, placeholder, label, value, type, meta: { touched, error } } = props;
return (
<fieldset className={`form-group ${ (touched && error) ? 'has-error' : '' }`}>
<label>{label}</label>
<input className="form-control" {...input} type={type} placeholder={placeholder} />
{touched && error && <div className="error">{error}</div>}
</fieldset>
);
}
renderSelect({ input, placeholder, options, label, type, meta: { touched, error } }) {
return (
<fieldset className={`form-group ${ (touched && error) ? 'has-error' : '' }`}>
<label>{label}</label>
<select className="form-control" {...input}>
{options}
</select>
{touched && error && <div className="error">{error}</div>}
</fieldset>
);
}
render() {
const { handleSubmit } = this.props;
const user = this.props.profile.user;
return (
<div> {user ? user.email : ''}
<form onSubmit={handleSubmit(this.handleEditProfileFormSubmit.bind(this))}>
<Field name="email" label="Email:" component={this.renderField} type="text" placeholder="email@gmail.com" className="form-control"/>
<Field name="name" label="Name:" component={this.renderField} type="text" placeholder="John Doe" className="form-control"/>
<Field name="role" label="Role:" component={this.renderSelect} type="select" className="form-control" options={this.getRoleOptions()}/>
<button action="submit" className="btn btn-primary">Edit user</button>
<Field name="password" label="Password:" component={this.renderField} type="password" className="form-control"/>
<Field name="passwordConfirm" label="Confirm Password:" component={this.renderField} type="password" className="form-control"/>
{ this.props.errorMessage
&& <div className="alert alert-danger">
<strong>Oops!</strong> {this.props.errorMessage}
</div> }
<button action="submit" className="btn btn-primary">Sign up!</button>
</form>
</div>
);
}
}
let InitializeFromStateForm = reduxForm({
form: 'initializeFromState'
})(UserEdit);
InitializeFromStateForm = connect(
state => ({
profile: state.profile,
initialValues: state.profile.user
}),
{ fetchRoleList, fetchUserData }
)(InitializeFromStateForm);
export default InitializeFromStateForm;
Oui croyez créateur d'action sera utile aussi bien:
export function fetchUserData(user_id) {
user_id = user_id ? user_id : '';
const authorization = localStorage.getItem('token');
const request = axios.get(`${ROOT_URL}/user/${user_id}`, {
headers: { authorization }
});
return {
type: FETCH_USER,
payload: request
};
}
5 réponses
Vous devez ajouter enableReinitialize: true comme ci-dessous.
let InitializeFromStateForm = reduxForm({
form: 'initializeFromState',
enableReinitialize : true // this is needed!!
})(UserEdit)
Si votre prop initialValues est mis à jour, votre formulaire sera également mis à jour.
Pour définir la initialValues
, il est important d'appliquer le reduxForm()
décorateur avant le connect()
décorateur de redux. Les champs ne seront pas remplis à partir de l'état du magasin si l'ordre des décorateurs est inversé.
const FormDecoratedComponent = reduxForm(...)(Component)
const ConnectedAndFormDecoratedComponent = connect(...)(FormDecoratedComponent)
Si, en plus de définir les valeurs pour la première fois, vous devez remplir à nouveau le formulaire chaque fois que l'état change, définissez enableReinitialize: true
Trouver un exemple simple dans cette réponse.
Lire la documentation officielle et complète exemple.
Lisez à propos de ce problème ici .
Donc, vous essayez:
- charger les données de L'API dans le formulaire
- mettre à Jour le formulaire juste en charge (aka.
initialValues
)
Alors que @FurkanO pourrait fonctionner, je pense que la meilleure approche est de charger le formulaire lorsque vous avez toutes les données asynchrones, vous pouvez le faire en créant un composant / conteneur parent:
UserEditLoader.jsx
componentDidMount() {
// I think this one fits best for your case, otherwise just switch it to
// componentDidUpdate
apiCalls();
}
/* api methods here */
render() {
const { profile } = this.props;
return (
{profile && <UserEdit profile={profile} />}
);
}
Fondamentalement, ce que vous devriez faire dans le UserEditLoader
est d'exécuter les fonctions de L'API et de mettre à jour l'état (ou les accessoires si redux est connecté). Chaque fois que l' la variable profile n'est pas vide (ce qui signifie que vous avez obtenu les données que vous attendiez) puis montez UserEdit
avec profile comme prop.
Si l'Astuce enableReinitialize : true
ne fonctionne pas, vous pouvez mettre à jour chaque champ lorsque l'accessoire initialValues
change.
componentWillReceiveProps(nextProps) {
const { change, initialValues } = this.props
const values = nextProps.initialValues;
if(initialValues !== values){
for (var key in values) {
if (values.hasOwnProperty(key)) {
change(key,values[key]);
}
}
}
}
Je n'ai jamais travaillé avec FieldsArray
mais je suppose que cela ne fonctionnerait pas ici.
Pour un composant fonctionnel sans état, vous pouvez le faire comme ceci:
componentWillMount() {
this.props.initialize({ discountCodes: ["ABC200", "XYZ500"] });
}
Pour une classe, vous pouvez le faire comme ceci:
const mapStateToProps = state => (
{
initialValues: {
discountCodes: ["ABC200", "XYZ500"]
}
);