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 certaines raisons, les champs restent vides quoi que je fasse.
La définition des valeurs fixes au lieu des valeurs transmises par le réducteur fonctionne bien pour une raison quelconque.
C'est peut-être 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 protected]" 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;
Je crois que créateur d'action sera utile aussi:
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
};
}
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 variable initialValues
, il est important d'appliquer le décorateur reduxForm()
avant le décorateur connect()
de Redux. Les champs ne seront pas renseignés à 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 changement d'état, définissez enableReinitialize: true
.
Trouvez un exemple simple dans cette réponse .
Lisez le document officiel et un exemple complet .
Lisez à propos de ce numéro ici .
Donc, vous essayez:
initialValues
)Bien que @FurkanO puisse fonctionner, je pense que la meilleure approche consiste à charger le formulaire lorsque vous avez toutes les données async. 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, dans la UserEditLoader
, vous devez exécuter les fonctions de l'API et mettre à jour l'état (ou les accessoires si redux est connecté). Chaque fois que la variable de profil n'est pas vide (ce qui signifie que vous avez obtenu les données que vous attendiez), montez UserEdit
avec le profil prop.
Si l'astuce enableReinitialize : true
ne fonctionne pas, vous pouvez mettre à jour chaque champ lorsque la propriété 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.
initialize () est un accessoire fourni par reduxForm, qui peut être utilisé pour remplir les valeurs du formulaire.
change () est un autre accessoire fourni par reduxFrom pour modifier une valeur de champ.
import * as React from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
const submit = values => {
// print the form values to the console
console.log(values)
}
interface Props {
history?: any;
location?: any;
session?: any;
handleSubmit?: Function;
initialize?: Function;
change?: Function;
}
class ContactForm extends React.Component<Props, any> {
constructor(props, state) {
super(props, state);
this.state = {
value: ''
};
}
componentDidMount() {
const { initialize, session, location } = this.props;
console.log(location.pathname);
if (session && session.user) {
const values = {
firstName: session.user.name,
lastName: session.user.lastName,
email: session.user.email
};
initialize(values);
}
}
componentWillReceiveProps(nextProps) {
const { initialize, session } = this.props;
if (nextProps.session !== session) {
if (nextProps.session && nextProps.session.user) {
const values = {
firstName: nextProps.session.user.name,
lastName: nextProps.session.user.lastName,
email: nextProps.session.user.email
};
initialize(values);
} else {
const values = {
firstName: null,
lastName: null,
email: null
};
initialize(values);
}
}
}
render() {
const { handleSubmit, change } = this.props;
return (
<React.Fragment>
<form onSubmit={handleSubmit(submit)}>
<div>
<label htmlFor="firstName">First Name</label>
<Field name="firstName" component="input" type="text" />
</div>
<div>
<label htmlFor="lastName">Last Name</label>
<Field name="lastName" component="input" type="text" />
</div>
<div>
<label htmlFor="email">Email</label>
<Field name="email" component="input" type="email" />
</div>
<button type="submit">Submit</button>
</form>
<input type="text" value={this.state.value}
onChange={(e) => {
this.setState({ value: e.target.value });
change('firstName', e.target.value);
}}
/>
</React.Fragment>
);
}
}
export default connect((state) => {
return {
session: state.session
}
},
{}
)(withRouter((reduxForm({
form: 'contact'
})(ContactForm))));