Qual é o padrão recomendado para fazer um setState em um pai a partir de um componente filho.
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)
Eu tenho uma série de itens de tarefas que são mantidos no estado do pai. Eu quero acessar o estado do pai e adicionar um novo item todo, a partir do TodoForm
's handleClick
componente. Minha ideia é fazer um setState no pai, que renderizará o item de tarefas recém-adicionado.
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.
Respostas:
Em seu pai, você pode criar uma função como a
addTodoItem
que fará o setState necessário e, em seguida, passar essa função como adereços para o componente filho.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: ""}); }, ... });
Você pode invocar
addTodoItem
no handleClick de TodoForm. Isso fará um setState no pai que renderizará o item de tarefas recém-adicionado. Espero que você tenha entendido.Fiddle aqui.
fonte
<<
operadora estáthis.state.todos << todoItem;
fazendo aqui?this.state
direta. Melhor usar setState funcional. reactjs.org/docs/react-component.html#setstateTudo isso está essencialmente correto, eu apenas pensei em apontar para a nova (ish) documentação oficial de reação que basicamente recomenda: -
Consulte https://reactjs.org/docs/lifting-state-up.html . A página também funciona por meio de um exemplo.
fonte
Você pode criar uma função addTodo no componente pai, vinculá-la a esse contexto, passá-la para o componente filho e chamá-la de lá.
// in Todos addTodo: function(newTodo) { // add todo }
Então, em Todos.render, você faria
<TodoForm addToDo={this.addTodo.bind(this)} />
Chame isso em TodoForm com
this.props.addToDo(newTodo);
fonte
bind(this)
na hora de passar função, estava jogando erro sem tal funçãothis.setState is not a function
.Para aqueles que estão mantendo o estado com o React Hook
useState
, adaptei as sugestões acima para fazer um aplicativo deslizante de demonstração abaixo. No aplicativo de demonstração, o componente deslizante filho mantém o estado do pai.A demonstração também usa
useEffect
gancho. (e 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;
fonte
useEffect
? Por que precisamos ter os dados armazenados no estado pai e filho?parentSetState={(obj) => { this.setState(obj) }}
fonte
Eu encontrei a seguinte solução simples e funcional para passar argumentos de um componente filho para o componente pai:
//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") ); }
Olhe para JSFIDDLE
fonte
Se você estiver trabalhando com um componente de classe como pai, uma maneira muito simples de passar um setState para um filho é passando-o dentro de uma função de seta. Isso funciona porque define um ambiente suspenso que pode ser transmitido:
class ClassComponent ... { modifyState = () =>{ this.setState({...}) } render(){ return <><ChildComponent parentStateModifier={modifyState} /></> } }
fonte