Подтвердить что ты не робот

Исходные значения Redux-Form из

Я пытаюсь заполнить форму профиля данными из API. К сожалению, редукс-форма не хочет сотрудничать со мной в этом случае. По какой-то причине поля остаются пустыми, что я делаю.

Задание фиксированных значений вместо значений, переданных из редуктора, хорошо работает по какой-либо причине.

Возможно, это потому, что я использую redux-prom для вызовов API внутри создателей действия? Как я могу жить с этим и избавиться от этого. Вот мой компонент формы.

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;

Я считаю, что создатель действия также будет полезен:

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
    };
}
4b9b3361

Ответ 1

Вам нужно добавить enableReinitialize: true, как показано ниже.

let InitializeFromStateForm = reduxForm({
    form: 'initializeFromState',
    enableReinitialize : true // this is needed!!
})(UserEdit)

Если ваша подпрограмма initialValues будет обновлена, ваша форма также обновится.

Ответ 2

Чтобы установить initialValues важно применить декоратор reduxForm() перед декоратором connect() из redux. Поля не будут заполняться из состояния хранилища, если порядок декораторов инвертирован.

const FormDecoratedComponent = reduxForm(...)(Component)
const ConnectedAndFormDecoratedComponent = connect(...)(FormDecoratedComponent)

Если, в дополнение к настройке значений в первый раз, вам необходимо повторно заполнять форму каждый раз при изменении состояния, тогда установите enableReinitialize: true

Найдите простой пример в этом ответе.

Прочитайте официальную документацию и полный пример.

Читайте об этой проблеме здесь.

Ответ 3

Итак, вы пытаетесь:

  • Загрузите данные API в форму
  • Обновите форму только при загрузке (ака. initialValues)

В то время как @FurkanO может работать, я считаю, что лучший способ - загрузить форму, когда вы получили все данные async, вы можете сделать это, создав родительский компонент/контейнер:

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} />}
 );
} 

В основном то, что вы должны делать в UserEditLoader, - это выполнение функций API и обновление состояния (или реквизита, если редукция подключена). Всякий раз, когда переменная профиля не пуста (что означает, что вы получили ожидаемые данные), установите UserEdit с профилем как prop.

Ответ 4

Если трюк enableReinitialize : true не работает, вы можете обновить каждое поле, когда изменяется initialValues.

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]);
            }
        }
    }
}

Я никогда не работал с FieldsArray, но я предполагаю, что это не сработает.

Ответ 5

initialize() - это опора, предоставляемая reduxForm, которую можно использовать для заполнения значений формы.

change() - это еще одна опора, предоставляемая reduxFrom для изменения значения поля.

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))));

Ответ 6

Для функционального компонента без сохранения состояния вы можете сделать это следующим образом:

componentWillMount() {
  this.props.initialize({ discountCodes: ["ABC200", "XYZ500"] });
}

Для класса вы можете сделать это так:

const mapStateToProps = state => (
  {
    initialValues: {
      discountCodes: ["ABC200", "XYZ500"]
  }
);