web-dev-qa-db-fra.com

Les valeurs initiales de Redux-Form à partir 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 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
    };
}
31
J33nn

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.

78
FurkanO

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 .

4
Arian Acosta

Donc, vous essayez:

  • Charger les données de l'API dans le formulaire
  • Mettre à jour le formulaire juste au chargement (aka. 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.

2
zurfyx

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. 

1
nbeuchat

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))));
0
Satyendra Singh