Как обновить состояние родителя в React?
моя структура выглядит следующим образом:
Component 1
- |- Component 2
- - |- Component 4
- - - |- Component 5
Component 3
компонент 3 должен отображать некоторые данные в зависимости от состояния компонента 5.
Поскольку реквизит неизменен, я не могу просто сохранить его состояние в компоненте 1 и переслать его, верно? И да, я читал о redux, но не хочу его использовать. Я надеюсь, что это можно решить только с реагировать. Я ошибаюсь?
7 ответов:
для связи ребенок-родитель вы должны передать функцию установки состояния от родителя к ребенку, как это
class Parent extends React.Component { constructor(props) { super(props) this.handler = this.handler.bind(this) } handler(e) { e.preventDefault() this.setState({ someVar: someValue }) } render() { return <Child handler = {this.handler} /> } } class Child extends React.Component { render() { return <Button onClick = {this.props.handler}/ > } }таким образом, ребенок может обновить состояние родителя с помощью вызова функции, переданной с реквизитами.
но вам придется переосмыслить структуру ваших компонентов, потому что, как я понимаю, компоненты 5 и 3 не связаны.
одно из возможных решений-обернуть их в компонент более высокого уровня, который будет содержит состояние обоих компонентов 1 и 3. Этот компонент установит состояние нижнего уровня через props.
Я нашел следующее рабочее решение для передачи аргумента функции onClick от дочернего к родительскому компоненту:
версия с передачей метода ()
//ChildB component class ChildB extends React.Component { render() { var handleToUpdate = this.props.handleToUpdate; return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div> ) } } //ParentA component class ParentA extends React.Component { constructor(props) { super(props); var handleToUpdate = this.handleToUpdate.bind(this); var arg1 = ''; } handleToUpdate(someArg){ alert('We pass argument from Child to Parent: ' + someArg); this.setState({arg1:someArg}); } render() { var handleToUpdate = this.handleToUpdate; return (<div> <ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>) } } if(document.querySelector("#demo")){ ReactDOM.render( <ParentA />, document.querySelector("#demo") ); }версия с передачей функции стрелой
//ChildB component class ChildB extends React.Component { render() { var handleToUpdate = this.props.handleToUpdate; return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div> ) } } //ParentA component class ParentA extends React.Component { constructor(props) { super(props); } handleToUpdate = (someArg) => { alert('We pass argument from Child to Parent: ' + someArg); } render() { return (<div> <ChildB handleToUpdate = {this.handleToUpdate} /></div>) } } if(document.querySelector("#demo")){ ReactDOM.render( <ParentA />, document.querySelector("#demo") ); }
мне нравится ответ относительно передачи функций вокруг, его очень удобная техника.
С другой стороны, вы также можете достичь этого с помощью pub / sub или с помощью варианта, диспетчера, как Flux делает. Теория супер проста, есть компонент 5 отправить сообщение, которое компонент 3 прослушивает. Затем компонент 3 обновляет свое состояние, которое запускает повторную визуализацию. Для этого требуются компоненты с отслеживанием состояния, которые, в зависимости от вашей точки зрения, могут быть или не быть антишаблон. Я лично против них и предпочел бы, чтобы что-то еще слушало депеши и меняло состояние с самого верха (Redux делает это, но добавляет дополнительную терминологию).
import { Dispatcher } from flux import { Component } from React const dispatcher = new Dispatcher() // Component 3 // Some methods, such as constructor, omitted for brevity class StatefulParent extends Component { state = { text: 'foo' } componentDidMount() { dispatcher.register( dispatch => { if ( dispatch.type === 'change' ) { this.setState({ text: 'bar' }) } } } render() { return <h1>{ this.state.text }</h1> } } // Click handler const onClick = event => { dispatcher.dispatch({ type: 'change' }) } // Component 5 in your example const StatelessChild = props => { return <button onClick={ onClick }>Click me</button> }связки диспетчера с Flux очень просты, он просто регистрирует обратные вызовы и вызывает их, когда происходит какая-либо отправка, проходя через содержимое на отправке (в приведенном выше кратком примере нет
payloadс отправкой, просто идентификатор сообщения). Вы могли бы адаптируйте это к традиционному пабу / sub (например, используя EventEmitter от events или какую-либо другую версию) очень легко, если это имеет для вас больше смысла.
Я нашел следующее рабочее решение для передачи аргумента функции onClick от дочернего к родительскому компоненту с помощью param:
родительский класс :
class Parent extends React.Component { constructor(props) { super(props) // Bind the this context to the handler function this.handler = this.handler.bind(this); // Set some state this.state = { messageShown: false }; } // This method will be sent to the child component handler(param1) { console.log(param1); this.setState({ messageShown: true }); } // Render the child component and set the action property with the handler as value render() { return <Child action={this.handler} /> }}дочерний класс :
class Child extends React.Component { render() { return ( <div> {/* The button will execute the handler function set by the parent component */} <Button onClick={this.props.action.bind(this,param1)} /> </div> ) } }
когда вам нужно общаться между ребенком и родителем на любом уровне вниз, то лучше использовать контекст. В Родительском компоненте определите контекст, который может быть вызван дочерним элементом, например
в Родительском компоненте в вашем случае компонент 3
static childContextTypes = { parentMethod: React.PropTypes.func.isRequired }; getChildContext() { return { parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child) }; } parentMethod(parameter_from_child){ // update the state with parameter_from_child }теперь в дочернем компоненте (компонент 5 в вашем случае), просто скажите это компонент, который он хочет использовать контекст его родитель.
static contextTypes = { parentMethod: React.PropTypes.func.isRequired }; render(){ return( <TouchableHighlight onPress={() =>this.context.parentMethod(new_state_value)} underlayColor='gray' > <Text> update state in parent component </Text> </TouchableHighlight> )}вы можете найти демо-проекта РЕПО
- мы можем создать ParentComponent и с помощью метода handleInputChange обновить состояние ParentComponent. Импортируем ChildComponent и передаем два реквизита от родительского к дочернему компоненту ie.функция handleInputChange и подсчет.
import React, { Component } from 'react'; import ChildComponent from './ChildComponent'; class ParentComponent extends Component { constructor(props) { super(props); this.handleInputChange = this.handleInputChange.bind(this); this.state = { count: '', }; } handleInputChange(e) { const { value, name } = e.target; this.setState({ [name]: value }); } render() { const { count } = this.state; return ( <ChildComponent count={count} handleInputChange={this.handleInputChange} /> ); } }
теперь мы создаем файл ChildComponent и сохраняем его как ChildComponent.jsx. Этот компонент не имеет состояния, так как дочерний компонент не имеет состояния. Мы используем библиотеку prop-types для типа реквизита проверочный.
import React from 'react'; import { func, number } from 'prop-types'; const ChildComponent = ({ handleInputChange, count }) => ( <input onChange={handleInputChange} value={count} name="count" /> ); ChildComponent.propTypes = { count: number, handleInputChange: func.isRequired, }; ChildComponent.defaultProps = { count: 0, }; export default ChildComponent;
<Footer action={()=>this.setState({showChart: true})} /> <footer className="row"> <button type="button" onClick={this.props.action}>Edit</button> {console.log(this.props)} </footer> Try this example to write inline setState, it avoids creating another function.
Comments