如何在React中更新父状态?

wkl*_*klm 298 javascript web-deployment reactjs

我的结构如下:

Component 1  

 - |- Component 2


 - - |- Component 4


 - - -  |- Component 5  

Component 3
Run Code Online (Sandbox Code Playgroud)

组件3应该根据组件5的状态显示一些数据.由于props是不可变的,我不能简单地将它保存在组件1中并转发它,对吧?是的,我读过有关redux的内容,但不想使用它.我希望只有反应就可以解决它.我错了吗?

Iva*_*van 573

对于子父通信,您应该传递一个函数,将状态从父级设置为子级,就像这样

class Parent extends React.Component {
  constructor(props) {
    super(props)

    this.handler = this.handler.bind(this)
  }

  handler() {
    this.setState({
      someVar: 'some value'
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {this.props.handler}/ >
  }
}
Run Code Online (Sandbox Code Playgroud)

这样,孩子可以通过调用带有props传递的函数来更新父级的状态.

但是你必须重新考虑组件的结构,因为据我所知,组件5和3不相关.

一种可能的解决方案是将它们包装在更高级别的组件中,该组件将包含组件1和3的状态.该组件将通过props设置较低级别的状态.

  • ES6中的@ chemook78 React类方法不会自动绑定到类.因此,如果我们不在构造函数中添加`this.handler = this.handler.bind(this)`,`handler`函数中的`this`将引用函数闭包,而不是类.如果不想在构造函数中绑定所有函数,还有两种方法可以使用箭头函数来处理它.您可以将单击处理程序编写为`onClick = {()=> this.setState(...)}`,或者您可以将属性初始化程序与箭头函数一起使用,如此处所述https://babeljs.io/blog/ 2015/06/07 /在"箭头功能"下的react-on-es6-plus (30认同)
  • 为什么你需要this.handler = this.handler.bind(this)而不仅仅是设置状态的处理函数? (6认同)
  • 这一切都有道理,为什么e.preventDefault?这需要jquery吗? (5认同)
  • 这是一个实际的例子:http://plnkr.co/edit/tGWecotmktae8zjS5yEr?p=preview (2认同)

Rom*_*man 43

我发现以下工作解决方案将onClick函数参数从child传递给父组件:

传递方法的版本()

//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")
    );
}
Run Code Online (Sandbox Code Playgroud)

看看JSFIDDLE

传递箭头功能的版本

//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")
    );
}
Run Code Online (Sandbox Code Playgroud)

看看JSFIDDLE


mos*_*ony 22

这就是我们如何使用新useState钩子来做到这一点。方法 - 将状态更改器函数作为道具传递给子组件,并对该函数执行您想做的任何操作

import React, {useState} from 'react';

const ParentComponent = () => {
  const[state, setState]=useState('');
  
  return(
    <ChildConmponent stateChanger={setState} />
  )
}


const ChildConmponent = ({stateChanger, ...rest}) => {
  return(
    <button onClick={() => stateChanger('New data')}></button>
  )
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的现代例子 (4认同)

Mat*_*les 9

我喜欢传递函数的答案,这是一种非常方便的技巧.

另一方面,你也可以使用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> 
}
Run Code Online (Sandbox Code Playgroud)

与Flux捆绑的调度程序非常简单,只需注册回调并在发生任何调度时调用它们,通过调度上的内容(在上面的简洁示例中,没有payload调度,只是消息ID).如果这对你更有意义,你可以很容易地将它改编为传统的pub/sub(例如,使用来自事件的EventEmitter或其他版本).

  • 我使用的语法是ES2016模块语法,需要转换(我使用[Babel](https://babeljs.io/),但还有其他的,[babelify](https://www.npmjs.com/package/ babelify)转换可以与browserify一起使用),它转换为`var Dispatcher = require('flux').Dispatcher` (2认同)

Ard*_*dhi 8

我要感谢最赞同的答案,它给了我自己的问题的想法,基本上是通过箭头功能和从子组件传递参数来实现的:

 class Parent extends React.Component {
  constructor(props) {
    super(props)
    // without bind, replaced by arrow func below
  }

  handler = (val) => {
    this.setState({
      someVar: val
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {() => this.props.handler('the passing value')}/ >
  }
}
Run Code Online (Sandbox Code Playgroud)

希望它可以帮助某人。

  • 这是一个重复的答案。您可以在已接受的答案中添加评论,并提及此“实验性”功能以在课堂上使用箭头函数。 (2认同)

lee*_*der 8

前面给出的大多数答案都是针对基于React.Component的设计。如果您正在使用useStateReact 库的最新升级,请遵循此答案


小智 6

我找到了以下可行的解决方案,以通过参数将onClick函数参数从子级传递到父级组件:

家长班:

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} />
}}
Run Code Online (Sandbox Code Playgroud)

子班:

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>
    )
} }
Run Code Online (Sandbox Code Playgroud)

  • 任何人都可以告诉我这是否是可以接受的解决方案(特别是对按建议传递参数感兴趣)。 (2认同)

小智 6

看起来我们只能将数据从父级传递到子级,因为 React 促进了单向数据流,但是为了让父级在其“子组件”发生某些事情时自行更新,我们通常使用所谓的“回调函数”。

我们将在父组件中定义的函数作为“道具”传递给子组件,并从子组件调用该函数在父组件中触发它。


class Parent extends React.Component {
  handler = (Value_Passed_From_SubChild) => {
    console.log("Parent got triggered when a grandchild button was clicked");
    console.log("Parent->Child->SubChild");
    console.log(Value_Passed_From_SubChild);
  }
  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <SubChild handler = {this.props.handler}/ >
  }
}

class SubChild extends React.Component {
  constructor(props){
   super(props);
   this.state = {
      somethingImp : [1,2,3,4]
   }
  }
  render() {
     return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
  }
}
React.render(<Parent />,document.getElementById('app'));

 HTML
 ----
 <div id="app"></div>
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我们可以让数据从子孩子传递?孩子parent通过将函数传递给它的直接孩子。


Raj*_*shu 5

每当您需要在任何级别的子级与父级之间进行通信时,最好使用 context。在父组件中,定义可以由子组件调用的上下文,例如

在您的案例组件的父组件中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
}
Run Code Online (Sandbox Code Playgroud)

现在在子组件(您的情况下为组件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>
)}
Run Code Online (Sandbox Code Playgroud)

您可以在回购中找到演示项目


小智 5

我多次使用此页面上评价最高的答案,但是在学习 React 的过程中,我找到了一种更好的方法来做到这一点,无需绑定,也无需在 props 中使用内联函数。

看看这里:

class Parent extends React.Component {

  constructor() {
    super();
    this.state = {
      someVar: value
    }
  }

  handleChange = (someValue) => {
    this.setState({someVar: someValue})
  }

  render() {
    return <Child handler={this.handleChange} />
  }

}

export const Child = ({handler}) => {
  return <Button onClick={handler} />
}
Run Code Online (Sandbox Code Playgroud)

关键在箭头函数中:

handleChange = (someValue) => {
  this.setState({someVar: someValue})
}
Run Code Online (Sandbox Code Playgroud)

您可以在此处阅读更多内容。


Pat*_*oco 5

如果同样的场景没有遍布各处,您可以使用 React 的上下文,特别是如果您不想引入状态管理库带来的所有开销。另外,它更容易学习。不过要小心; 你可能会过度使用它并开始编写糟糕的代码。基本上,您定义一个容器组件(它将为您保留并保留该状态),使所有组件都对向其子级(不一定是直接子级)写入/读取该数据感兴趣。

上下文 - 反应

您也可以正确使用普通的 React。

<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />
Run Code Online (Sandbox Code Playgroud)

将 doSomethingAbout5 传递给组件 1:

<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />
Run Code Online (Sandbox Code Playgroud)

如果这是一个常见问题,您应该开始考虑将应用程序的整个状态移动到其他地方。您有几种选择,最常见的是:

基本上,您不是在组件中管理应用程序状态,而是在发生某些情况更新状态时发送命令。组件也从该容器中提取状态,因此所有数据都是集中的。这并不意味着您不能再使用本地状态,但这是一个更高级的主题。


小智 5

只需通过 props 将父组件的 setState 函数传递给子组件即可。

function ParentComp() {
  const [searchValue, setSearchValue] = useState("");
  return <SearchBox setSearchValue={setSearchValue} searchValue={searchValue} />;
}
Run Code Online (Sandbox Code Playgroud)

然后在子组件中:

function SearchBox({ searchValue, setSearchValue }) {
  return (
        <input
          id="search-post"
          type="text"
          value={searchValue}
          onChange={(e) => setSearchValue(e.target.value)}
          placeholder="Search Blogs ..."
        />
    )
}
Run Code Online (Sandbox Code Playgroud)

处理子组件点击的第二个示例:

// We've below function and component in parent component
const clickHandler = (val) => {
  alert(`httpRequest sent. \nValue Received: ${val}`);
};

// JSX
<HttpRequest clickHandler={clickHandler} />
Run Code Online (Sandbox Code Playgroud)

这就是从父组件获取函数然后传递一个值并clickHandler通过它触发的方式。

function HttpRequest({ clickHandler }) {
  const [content, setContent] = useState("initialState");

  return (
    <button onClick={() => clickHandler(content)}>
      Send Request
    </button>
  );
}

export default HttpRequest;
Run Code Online (Sandbox Code Playgroud)