¿Cuál es el patrón recomendado para hacer un setState en un componente principal desde un componente secundario?
var Todos = React.createClass({
getInitialState: function() {
return {
todos: [
"I am done",
"I am not done"
]
}
},
render: function() {
var todos = this.state.todos.map(function(todo) {
return <div>{todo}</div>;
});
return <div>
<h3>Todo(s)</h3>
{todos}
<TodoForm />
</div>;
}
});
var TodoForm = React.createClass({
getInitialState: function() {
return {
todoInput: ""
}
},
handleOnChange: function(e) {
e.preventDefault();
this.setState({todoInput: e.target.value});
},
handleClick: function(e) {
e.preventDefault();
//add the new todo item
},
render: function() {
return <div>
<br />
<input type="text" value={this.state.todoInput} onChange={this.handleOnChange} />
<button onClick={this.handleClick}>Add Todo</button>
</div>;
}
});
React.render(<Todos />, document.body)
Tengo una variedad de elementos pendientes que se mantienen en el estado de los padres. Quiero tener acceso a estado de los padres y añadir un nuevo elemento de tarea, desde la TodoForm
's handleClick
componentes. Mi idea es hacer un setState en el padre, que renderizará el elemento de tareas recién agregado.
setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the MyModal component.
Respuestas:
En su padre, puede crear una función como la
addTodoItem
que hará el setState requerido y luego pasar esa función como accesorios al componente hijo.var Todos = React.createClass({ ... addTodoItem: function(todoItem) { this.setState(({ todos }) => ({ todos: { ...todos, todoItem } })); }, render: function() { ... return <div> <h3>Todo(s)</h3> {todos} <TodoForm addTodoItem={this.addTodoItem} /> </div> } }); var TodoForm = React.createClass({ handleClick: function(e) { e.preventDefault(); this.props.addTodoItem(this.state.todoInput); this.setState({todoInput: ""}); }, ... });
Puede invocar
addTodoItem
en el handleClick de TodoForm. Esto hará un setState en el padre que renderizará el elemento de tarea recién agregado. Espero que entiendas la idea.Violín aquí.
fuente
<<
operadorthis.state.todos << todoItem;
aquí?this.state
directamente. Es mejor usar setState funcional. reactjs.org/docs/react-component.html#setstateTodos estos son esencialmente correctos, solo pensé que señalaría la nueva (ish) documentación oficial de reacción que básicamente recomienda: -
Consulte https://reactjs.org/docs/lifting-state-up.html . La página también funciona a través de un ejemplo.
fuente
Puede crear una función addTodo en el componente principal, vincularla a ese contexto, pasarla al componente secundario y llamarla desde allí.
// in Todos addTodo: function(newTodo) { // add todo }
Entonces, en Todos.render, harías
<TodoForm addToDo={this.addTodo.bind(this)} />
Llame a esto en TodoForm con
this.props.addToDo(newTodo);
fuente
bind(this)
en el momento de pasar la función, estaba arrojando un error sin tal funciónthis.setState is not a function
.Para aquellos que mantienen el estado con React Hook
useState
, adapté las sugerencias anteriores para hacer una aplicación de control deslizante de demostración a continuación. En la aplicación de demostración, el componente deslizante secundario mantiene el estado del padre.La demostración también usa
useEffect
hook. (y menos importante,useRef
gancho)import React, { useState, useEffect, useCallback, useRef } from "react"; //the parent react component function Parent() { // the parentState will be set by its child slider component const [parentState, setParentState] = useState(0); // make wrapper function to give child const wrapperSetParentState = useCallback(val => { setParentState(val); }, [setParentState]); return ( <div style={{ margin: 30 }}> <Child parentState={parentState} parentStateSetter={wrapperSetParentState} /> <div>Parent State: {parentState}</div> </div> ); }; //the child react component function Child({parentStateSetter}) { const childRef = useRef(); const [childState, setChildState] = useState(0); useEffect(() => { parentStateSetter(childState); }, [parentStateSetter, childState]); const onSliderChangeHandler = e => { //pass slider's event value to child's state setChildState(e.target.value); }; return ( <div> <input type="range" min="1" max="255" value={childState} ref={childRef} onChange={onSliderChangeHandler} ></input> </div> ); }; export default Parent;
fuente
useEffect
? ¿Por qué necesitamos tener los datos almacenados tanto en el estado principal como en el secundario?parentSetState={(obj) => { this.setState(obj) }}
fuente
Encontré la siguiente solución funcional y simple para pasar argumentos de un componente secundario al componente principal:
//ChildExt component class ChildExt extends React.Component { render() { var handleForUpdate = this.props.handleForUpdate; return (<div><button onClick={() => handleForUpdate('someNewVar')}>Push me</button></div> ) } } //Parent component class ParentExt extends React.Component { constructor(props) { super(props); var handleForUpdate = this.handleForUpdate.bind(this); } handleForUpdate(someArg){ alert('We pass argument from Child to Parent: \n' + someArg); } render() { var handleForUpdate = this.handleForUpdate; return (<div> <ChildExt handleForUpdate = {handleForUpdate.bind(this)} /></div>) } } if(document.querySelector("#demo")){ ReactDOM.render( <ParentExt />, document.querySelector("#demo") ); }
Mira JSFIDDLE
fuente
Si está trabajando con un componente de clase como padre, una forma muy sencilla de pasar un setState a un hijo es pasándolo dentro de una función de flecha. Esto funciona ya que establece un entorno elevado que se puede pasar:
class ClassComponent ... { modifyState = () =>{ this.setState({...}) } render(){ return <><ChildComponent parentStateModifier={modifyState} /></> } }
fuente