React兄弟、父子元素之间的通信
React兄弟、父子元素之间的通信
React元素之间的通信主要由下面几种方式
1、 Redux
2、 EventEmitter
3、 通过props进行通信(需要有嵌套关系)
子元素到父元素
父子元素之间的通信主要靠props
,这个方法既简单,又好用,所以可以使用这种方法的时候就直接用好了。
首先有这样的一个React DOM结构:
<div className="passage"> <NavBar /> <Passage /> </div>
渲染外层的div
元素的时候,需要进行两个子组件的渲染,其中Passage
组件的加载内容取决于NavBar
当前的内容或者被点击后的内容,这里可以首先实现父元素和NavBar
之间的通信过程,设置一个句柄,来帮助进行通信。
constructor (props) { super(props); this.state = { currentPassage: "" } this.refreshCurrentPassage = this.refreshCurrentPassage.bind(this); } refreshCurrentPassage(cp) { this.setState({ currentPassage: cp }); }
上面的refreshCurrentPassage
函数是这个通信过程的关键,我们将这个函数绑定当前父元素的this
对象,并且将这个函数通过props
传递给子元素:
render() { return ( <div className="passage"> <NavBar refreshCurrentPassage={this.refreshCurrentPassage} /> <Passage currentPassage={this.state.currentPassage}/> </div> ); }
子元素在其生命周期的componentDidMount
阶段通过ajax获取当前页信息并且调用这个函数,将消息传递给父元素,这里需要注意React的生命周期,componentDidMount
在官方给出的解释是:
Invoked once, only on the client (not on the server), immediately after the initial rendering occurs.
注意是在初始化渲染之后执行一次的。如果你还有其他特殊需求的话可以在其他阶段来调用这个函数。
class NavUl extends React.Component { constructor (props) { super(props); this.state = { passage: [] } } componentDidMount() { //在ajax请求成功之后调用一次更新父元素状态的函数,来完成一次父子元素之间的通信 $.ajax({ url: "../resources/data/passage.json", success: (data) => { this.setState({ passage: data.passages }); this.props.refreshCurrentPassage(data.passages[0].passageName); }、 }); } render() { let liArray = []; this.state.passage.forEach((value,index) => { let liEle = <li key={value.passageName.toString()}> <a> {value.passageName}({value.letterNum}) author:{value.author} </a> </li> liArray.push(liEle); }); return ( <ul> {liArray} </ul> ) } } class NavBar extends React.Component { constructor (props) { super(props); } render() { return ( <nav> <NavUl refreshCurrentPassage={this.props.refreshCurrentPassage}/> </nav> ); } }
父元素到子元素
父元素到子元素的状态通信更加简单了,可以直接使用props
来进行传递。
//父元素的render函数 render() { return ( <div className="passage"> <NavBar refreshCurrentPassage={this.refreshCurrentPassage} /> <Passage currentPassage={this.state.currentPassage}/> </div> ); }
通过将自己的state的currentPassage属性传递给子元素的一个属性,也就是props
对象,来告诉子元素加载某个模块。
class Passage extends React.Component { constructor (props) { super(props); this.state = { passage: "" } } //子元素通过自己的this.props对象来进行访问,可以直接得到父元素传递的消息 componentWillReceiveProps (nextProps) { let passageName = nextProps.currentPassage; passageName = passageName.toLowerCase().replace(" ", ""); $.ajax({ url: "../resources/data/" + passageName + ".json", success: (data) => { this.setState({ passage: data.passageContent }); } }); } render() { return ( <article> <p> {this.state.passage} </p> </article> ); } }
兄弟元素之间
上面的两个部分组合起来刚好就完成了兄弟元素之前的通信,其中一个子元素通过调用父元素传递过来的函数this.props.refreshCurrentPassage
来修改父元素状态,然后在父元素状态修改之后,另外一个子元素的this.props
会发生变化,从而触发重新渲染。这里需要注意的是React组件的声明周期,父元素到子元素通信由于可能需要多次渲染,所以使用了声明周期中的componentWillReceiveProps
阶段来进行内容加载。在官方文档中是这么介绍这个阶段的:
Invoked when a component is receiving new props. This method is not called for the initial render.
也就是在一个组件接收了新的属性(props
)触发的,这个方法不会在初始化渲染的时候触发。并且这个阶段还可以访问修改前的props
对象。