reactjs - helmet - Redux-Formar los valores iniciales de.
react router (5)
Estoy tratando de llenar el formulario de perfil con datos de API. Desafortunadamente, redux-form no quiere cooperar conmigo en este caso. Por alguna razón los campos se quedan vacíos lo que hago.
Establecer los valores fijos en lugar de los valores pasados desde el reductor funciona bien por alguna razón.
Tal vez esto se deba a que estoy usando la promesa de redux para llamadas a la API dentro de los creadores de acciones? ¿Cómo puedo vivir con eso y deshacerme de esto? Aquí está mi componente de formulario.
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;
Creo que el creador de acción también será útil:
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
};
}
Debe agregar enableReinitialize: true como se muestra a continuación.
let InitializeFromStateForm = reduxForm({
form: ''initializeFromState'',
enableReinitialize : true // this is needed!!
})(UserEdit)
Si su prop inicialValores se actualiza, su formulario también se actualizará.
Entonces, estás intentando:
- Cargar datos de API en el formulario
- Actualizar el formulario solo en carga (aka.
initialValues
)
Si bien @FurkanO podría funcionar, creo que el mejor enfoque es cargar el formulario cuando obtuviste todos los datos asíncronos, puedes hacerlo creando un componente / contenedor principal:
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} />}
);
}
Básicamente, lo que deberías hacer en UserEditLoader
es ejecutar las funciones de la API y actualizar el estado (o accesorios si Redux conectado). Cuando la variable del perfil no esté vacía (lo que significa que obtuvo los datos que esperaba), monte UserEdit
con el perfil como prop.
Para establecer los initialValues
es importante aplicar el decorador reduxForm()
antes del decorador connect()
desde redux. Los campos no se rellenarán desde el estado de la tienda si se invierte el orden de los decoradores.
const FormDecoratedComponent = reduxForm(...)(Component)
const ConnectedAndFormDecoratedComponent = connect(...)(FormDecoratedComponent)
Si, además de configurar los valores por primera vez, necesita volver a llenar el formulario cada vez que cambie el estado, configure enableReinitialize: true
Encuentra un ejemplo simple en esta respuesta .
Lea la documentación oficial y el ejemplo completo .
Lea sobre este tema here .
Para un componente funcional sin estado, puede hacerlo así:
componentWillMount() {
this.props.initialize({ discountCodes: ["ABC200", "XYZ500"] });
}
Para una clase, puedes hacerlo así:
const mapStateToProps = state => (
{
initialValues: {
discountCodes: ["ABC200", "XYZ500"]
}
);
Si el truco enableReinitialize : true
no funciona, puede actualizar cada campo cuando initialValues
prop 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]);
}
}
}
}
Nunca he trabajado con FieldsArray
pero asumo que esto no funcionaría aquí.