javascript - instalar - ¿Cómo acceder al estado del niño en React?
react js init (3)
Tengo la siguiente estructura:
FormEditor
- contiene múltiples FieldEditor
FieldEditor
- edita un campo del formulario y guarda varios valores al respecto en su estado
Cuando se hace clic en un botón dentro de FormEditor, quiero poder recopilar información sobre los campos de todos los componentes de
FieldEditor
, información que está en su estado, y tenerlo todo dentro de FormEditor.
Pensé en almacenar la información sobre los campos fuera del
FieldEditor
de
FieldEditor
y en su lugar la puse en el estado de
FormEditor
.
Sin embargo, eso requeriría que
FormEditor
escuchara cada uno de
FieldEditor
componentes
FieldEditor
medida que cambian y almacenan su información en su estado.
¿No puedo acceder al estado de los niños en su lugar? ¿Es ideal?
Ahora puede acceder al estado de InputField que es el hijo de FormEditor.
Básicamente, cada vez que hay un cambio en el estado del campo de entrada (secundario), obtenemos el valor del objeto de evento y luego pasamos este valor al Principal donde se establece el estado en el Principal.
Al hacer clic en el botón, solo estamos imprimiendo el estado de los campos de entrada.
El punto clave aquí es que estamos usando los accesorios para obtener el id / valor del campo de entrada y también para llamar a las funciones que se establecen como atributos en el campo de entrada mientras generamos los campos de entrada secundarios reutilizables.
class InputField extends React.Component{
handleChange = (event)=> {
const val = event.target.value;
this.props.onChange(this.props.id , val);
}
render() {
return(
<div>
<input type="text" onChange={this.handleChange} value={this.props.value}/>
<br/><br/>
</div>
);
}
}
class FormEditorParent extends React.Component {
state = {};
handleFieldChange = (inputFieldId , inputFieldValue) => {
this.setState({[inputFieldId]:inputFieldValue});
}
//on Button click simply get the state of the input field
handleClick = ()=>{
console.log(JSON.stringify(this.state));
}
render() {
const fields = this.props.fields.map(field => (
<InputField
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
<div>
<button onClick={this.handleClick}>Click Me</button>
</div>
<div>
{fields}
</div>
</div>
);
}
}
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditorParent fields={fields} />;
};
ReactDOM.render(<App/>, mountNode);
Justo antes de entrar en detalles sobre cómo puede acceder al estado de un componente secundario, asegúrese de leer la respuesta de Markus-ipse respecto a una mejor solución para manejar este escenario en particular.
Si realmente desea acceder al estado de los elementos secundarios de un componente, puede asignar una propiedad llamada
ref
a cada elemento secundario.
Ahora hay dos formas de implementar referencias: utilizando
React.createRef()
y referencias de devolución de llamada.
Usando
React.createRef()
Esta es actualmente la forma recomendada de utilizar referencias a partir de React 16.3 (consulte los documentos para obtener más información). Si está utilizando una versión anterior, consulte a continuación las referencias de devolución de llamada.
Deberá crear una nueva referencia en el constructor de su componente principal y luego asignarla a un elemento secundario a través del atributo
ref
.
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.FieldEditor1 = React.createRef();
}
render() {
return <FieldEditor ref={this.FieldEditor1} />;
}
}
Para acceder a este tipo de referencia, deberá usar:
const currentFieldEditor1 = this.FieldEditor1.current;
Esto devolverá una instancia del componente montado para que pueda usar
currentFieldEditor1.state
para acceder al estado.
Solo una nota rápida para decir que si usa estas referencias en un nodo DOM en lugar de un componente (por ejemplo,
<div ref={this.divRef} />
),
this.divRef.current
devolverá el elemento DOM subyacente en lugar de un componente ejemplo.
Referencia de devolución de llamada
Esta propiedad toma una función de devolución de llamada a la que se le pasa una referencia al componente adjunto. Esta devolución de llamada se ejecuta inmediatamente después de que el componente esté montado o desmontado.
Por ejemplo:
<FieldEditor
ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
{...props}
/>
En estos ejemplos, la referencia se almacena en el componente principal. Para llamar a este componente en su código, puede usar:
this.fieldEditor1
y luego use
this.fieldEditor1.state
para obtener el estado.
Una cosa a tener en cuenta, asegúrese de que su componente hijo se haya procesado antes de intentar acceder a él ^ _ ^
Como se
<div ref={(divRef) => {this.myDiv = divRef;}} />
anteriormente, si utiliza estas referencias en un nodo DOM en lugar de un componente (por ejemplo,
<div ref={(divRef) => {this.myDiv = divRef;}} />
),
this.divRef
devolverá el elemento DOM subyacente en lugar de una instancia de componente.
Más información
Si desea leer más sobre la propiedad de referencia de React, consulte esta page de Facebook.
Asegúrese de leer la sección "No usar en
exceso las referencias
" que dice que no debe usar el
state
del niño para "hacer que las cosas sucedan".
Espero que esto ayude ^ _ ^
Editar: Se
React.createRef()
método
React.createRef()
para crear referencias.
Se eliminó el código ES5.
Si ya tiene el controlador onChange para los FieldEditors individuales, no entiendo por qué no puede simplemente mover el estado al componente FormEditor y simplemente pasar una devolución de llamada desde allí a los FieldEditors que actualizarán el estado principal. Para mí, esa es una forma más Reactiva de hacerlo.
Algo en la línea de esto quizás:
class FieldEditor extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const text = event.target.value;
this.props.onChange(this.props.id, text);
}
render() {
return (
<div className="field-editor">
<input onChange={this.handleChange} value={this.props.value} />
</div>
);
}
}
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleFieldChange = this.handleFieldChange.bind(this);
}
handleFieldChange(fieldId, value) {
this.setState({ [fieldId]: value });
}
render() {
const fields = this.props.fields.map(field => (
<FieldEditor
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
{fields}
<div>{JSON.stringify(this.state)}</div>
</div>
);
}
}
// Convert to class component and add ability to dynamically add/remove fields by having it in state
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditor fields={fields} />;
};
ReactDOM.render(<App />, document.body);
http://jsbin.com/qeyoxobixa/edit?js,output
Editar: solo por el gusto de hacerlo, he reescrito el ejemplo anterior usando ganchos para cualquier persona interesada:
const FieldEditor = ({ value, onChange, id }) => {
const handleChange = event => {
const text = event.target.value;
onChange(id, text);
};
return (
<div className="field-editor">
<input onChange={handleChange} value={value} />
</div>
);
};
const FormEditor = props => {
const [values, setValues] = useState({});
const handleFieldChange = (fieldId, value) => {
setValues({ ...values, [fieldId]: value });
};
const fields = props.fields.map(field => (
<FieldEditor
key={field}
id={field}
onChange={handleFieldChange}
value={values[field]}
/>
));
return (
<div>
{fields}
<pre>{JSON.stringify(values, null, 2)}</pre>
</div>
);
};
// To add abillity to dynamically add/remove fields keep the list in state
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditor fields={fields} />;
};