在编写 react 组件时,经常会遇到一个场景:子组件有个状态,可以通过内部的一个按钮进行切换;而父组件也可以通过一个按钮,同步去切换子组件的状态。比如侧边栏菜单的实现:顶部导航通过点击“筛选”按钮展开侧边栏,而侧边栏自身通过点击“箭头”按钮收起侧边栏。在这种场景下,当点击“筛选”按钮时,则是父组件将改变后的状态传递给子组件;而点击“箭头”按钮时,则是子组件自身状态的变化,同时也把这个状态传递回父组件。
这是一个相当典型的父子组件数值的双向传递,本文将以上面场景为例讲解如何实现双向传递。
要实现侧边栏的功能,需要先了解父子组件各自单向传递的方式。
父组件传值给子组件,主要是通过 props 的方式进行处理。具体示例如下:
//父组件
class ParentCom extends Component {
constructor(props) {
...
}
render() {
const title="传给子组件的标题";
return (
<div className={"parent"}>
<ChildCom
title={title}
/>
</div>
);
}
}
//子组件
class ChildCom extends Component {
constructor(props) {
...
}
render() {
const {
title
} = this.props;
return (
<div className="child">
<h3>{title}</h3>
</div>
);
}
}
父组件在 render 函数中定义了变量 title ,然后通过把这个变量赋值给子组件的 title 属性中。而在子组件中,在 render 函数中通过 react 的 props 对象取到刚传递过来的值。
子组件传值给父组件,主要是通过调用父组件传递过来的回调函数来实现的。具体示例如下:
//父组件
class ParentCom extends Component {
constructor(props) {
...
this.callback = this.callback.bind(this);
}
callback(childState){
// 记录下子组件传递过来的值
this.childState = childState;
}
render() {
const title="传给子组件的标题";
return (
<div className={"parent"}>
<ChildCom
title={title}
callback={this.callback}
/>
</div>
);
}
}
//子组件
class ChildCom extends Component {
constructor(props) {
...
this.state = {
show: false
}
this.showTrigger = this.showTrigger.bind(this);
}
showTrigger(){
const {
callback
} = this.props;
const show = !this.state.show;
this.setState({
show: show
});
if (callback) {
// 将子组件改变后的状态值传给父级
callback(show);
}
}
render() {
const {
title
} = this.props;
return (
<div className="child">
<h3>{title}</h3>
<button
onClick={this.showTrigger}
>
改变子组件的状态
</button>
</div>
);
}
}
父组件定义了一个回调函数 callback,用于接收子组件状态值。这里要注意的一点是,在 constructor 中通过 bind 方法将 callback 中的 this 强制指向父组件。这一步很关键,这是保证子组件执行回调函数时,能够访问父组件的关键。
而子组件通过 props 获得回调函数后,在改变状态时,将改变后的状态值通过回调函数的参数传递给父组件。
了解了各自的单向传递后,要实现侧边栏的功能就很简单了。只需要将以下两个步骤合并在一起即可以实现。主要实现以下两个流程:
点击“筛选”按钮 》改变父组件记录的侧边栏展开状态,并触发父组件自身状态值的改变 》父组件重新渲染 》通过 props 传值给侧边栏 》侧边栏重新渲染,执行展开动作
//父组件
class ParentCom extends Component {
constructor(props) {
...
this.state = {
childState: false
}
this.childState = false;
this.childStateTrigger = this.childStateTrigger.bind(this);
}
childStateTrigger(){
const childState = !this.childState;
this.setState({
childState: childState
});
this.childState = childState;
}
render() {
const title="传给子组件的标题";
const {
childState
} = this.state;
return (
<div className={"parent"}>
<ChildCom
title={title}
show={childState}
/>
<button
onClick={this.childStateTrigger}
>筛选</button>
</div>
);
}
}
//子组件
class ChildCom extends Component {
constructor(props) {
...
this.state = {
show: false
}
}
componentWillReceiveProps(nextProps){
//接收从父级传递过来的值
if ('show' in nextProps) {
this.setState({
show: nextProps.show
});
}
}
render() {
const {
title
} = this.props;
const {
show
} = this.state;
return (
<div className="child">
<h3>{title}</h3>
<p>当前子组件的展示状态是:{show.toString()}</p>
</div>
);
}
}
点击“箭头”按钮 》 将侧边栏的展开状态变成收起状态,并调用父组件的回调函数 》 父组件在回调函数中,记录下子组件的状态值。具体代码如下:
//父组件
class ParentCom extends Component {
constructor(props) {
...
this.state = {
childState: false
}
this.childState = false;
this.childStateTrigger = this.childStateTrigger.bind(this);
this.callback = this.callback.bind(this);
}
childStateTrigger(){
const childState = !this.childState;
this.setState({
childState: childState
});
this.childState = childState;
}
callback(show){
this.childState = show;
console.log('更新父级的记录:', show);
}
render() {
const title="传给子组件的标题";
const {
childState
} = this.state;
return (
<div className={"parent"}>
<ChildCom
title={title}
show={childState}
callback={this.callback}
/>
<button
onClick={this.childStateTrigger}
>筛选</button>
</div>
);
}
}
//子组件
class ChildCom extends Component {
constructor(props) {
...
this.state = {
show: false
}
this.showTrigger = this.showTrigger.bind(this);
}
componentWillReceiveProps(nextProps){
//接收从父级传递过来的值
if ('show' in nextProps) {
this.setState({
show: nextProps.show
});
}
}
showTrigger(){
const {
callback
} = this.props;
const show = !this.state.show;
this.setState({
show: show
});
// 将子组件状态的改变传回父组件
if (callback) {
callback(show);
}
}
render() {
const {
title
} = this.props;
const {
show
} = this.state;
return (
<div className="child">
<h3>{title}</h3>
<p>当前子组件的展示状态是:{show.toString()}</p>
<button
onClick={this.showTrigger}
>收起</button>
</div>
);
}
}
这里要一点要注意,在父组件的回调函数中,并没有把子组件的状态直接记录到父组件对应的状态值中。这是因为,对于子组件状态的变化,父组件只需要记录下就可以了,并不需要再次做重新的渲染。而且如果直接改变父组件的状态,则会引发父组件的重新渲染,从而触发侧边栏的属性传递。这一步虽然不会消耗多少性能,但显然是没有必要的过程。因此是通过 this.childState 的方式记录下侧边栏传递过来的状态值。
通过以上两步,就实现了完成的侧边样的收起与展开的功能。其他父子组件数值的双向传递都可以参考这种方式进行处理。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。