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
    };
}
27
demandé sur J33nn 2016-12-21 19:01:37

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.

66
répondu FurkanO 2016-12-21 19:03:44

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 .

4
répondu Arian Acosta 2018-03-06 19:53:47

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.

2
répondu zurfyx 2016-12-21 22:09:26

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.

0
répondu nbeuchat 2017-11-22 03:48:41

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"]
  }
);
-1
répondu Justin Noel 2018-09-22 13:14:48