闲话不多说,开篇撸代码,你可以会看到类似如下的结构:

import React, { Component } from 'react';

// 父组件
class Parent extends Component {
constructor() {
super();
this.state = { color: 'red' };
} render() {
return <Child1 { ...this.props } />
}
} // 子组件1
const Child1 = props => {
return <Child2 { ...props } />
} // 子组件2
const Child2 = props => {
return <Child3 { ...props } />
} // 子组件3
const Child3 = props => {
return <div style={{ color: props.color }}>Red</div>
}

See the Pen react props by 糊一笑 (@rynxiao) on CodePen.

当一个组件嵌套了若干层子组件时,而想要在特定的组件中取得父组件的属性,就不得不将props一层一层地往下传,我这里只是简单的列举了3个子组件,而当子组件嵌套过深的时候,props的维护将成噩梦级增长。因为在每一个子组件上你可能还会对传过来的props进行加工,以至于你最后都不确信你最初的props中将会有什么东西。

那么React中是否还有其他的方式来传递属性,从而改善这种层层传递式的属性传递。答案肯定是有的,主要还有以下两种形式:

Redux等系列数据仓库

使用Redux相当于在全局维护了整个应用数据的仓库,当数据改变的时候,我们只需要去改变这个全局的数据仓库就可以了。类似这样的:

var state = {
a: 1
}; // index1.js
state.a = 2; // index2.js
console.log(state.a); // 2

当然这只是一种非常简单的形式解析,Reudx中的实现逻辑远比这个要复杂得多,有兴趣可以去深入了解,或者看我之前的文章:用react+redux编写一个页面小demo以及react脚手架改造,下面大致列举下代码:

// actions.js
function getA() {
return {
type: GET_DATA_A
};
} // reducer.js
const state = {
a: 1
}; function reducer(state, action) {
case GET_DATA_A:
state.a = 2;
return state;
default:
return state;
} module.exports = reducer; // Test.js
class Test extends React.Component {
constructor() {
super();
} componentDidMount() {
this.props.getA();
}
} export default connect(state => {
return { a: state.reducer.a }
}, dispatch => {
return { getA: dispatch => dispatch(getA()) }
})(Test);

这样当在Test中的componentDidMount中调用了getA()之后,就会发送一个action去改变store中的状态,此时的a已经由原先的1变成了2。

这只是一个任一组件的大致演示,这就意味着你可以在任何组件中来改变store中的状态。关于什么时候引入redux我觉得也要根据项目来,如果一个项目中大多数时候只是需要跟组件内部打交道,那么引入redux反而造成了一种资源浪费,更多地引来的是学习成本和维护成本,因此并不是说所有的项目我都一定要引入redux。

context

关于context的讲解,React文档中将它放在了进阶指引里面。具体地址在这里:https://reactjs.org/docs/context.html。主要的作用就是为了解决在本文开头列举出来的例子,为了不让props在每层的组件中都需要往下传递,而可以在任何一个子组件中拿到父组件中的属性。

但是,好用的东西往往也有副作用,官方也给出了几点不要使用context的建议,如下:

  • 如果你想你的应用处于稳定状态,不要用context
  • 如果你不太熟悉Redux或者MobX等状态管理库,不要用context
  • 如果你不是一个资深的React开发者,不要用context

鉴于以上三种情况,官方更好的建议是老老实实使用props和state。

下面主要大致讲一下context怎么用,其实在官网中的例子已经十分清晰了,我们可以将最开始的例子改一下,使用context之后是这样的:

class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { color: 'red' };
} getChildContext() {
return { color: this.state.color }
} render() {
return <Child1 />
}
} const Child1 = () => {
return <Child2 />
} const Child2 = () => {
return <Child3 />
} const Child3 = ({ children }, context) => {
console.log('context', context);
return <div style={{ color: context.color }}>Red</div>
} Parent.childContextTypes = {
color: PropTypes.string
}; Child3.contextTypes = {
color: PropTypes.string
}; ReactDOM.render(<Parent />, document.getElementById('container'));

可以看到,在子组件中,所有的{ ...props }都不需要再写,只需要在Parent中定义childContextTypes的属性类型,以及定义getChildContext钩子函数,然后再特定的子组件中使用contextTypes接收即可。

See the Pen react context by 糊一笑 (@rynxiao) on CodePen.

这样做貌似十分简单,但是你可能会遇到这样的问题:当改变了context中的属性,但是由于并没有影响父组件中上一层的中间组件的变化,那么上一层的中间组件并不会渲染,这样即使改变了context中的数据,你期望改变的子组件中并不一定能够发生变化,例如我们在上面的例子中再来改变一下:

// Parent
render() {
return (
<div className="test">
<button onClick={ () => this.setState({ color: 'green' }) }>change color to green</button>
<Child1 />
</div>
)
}

增加一个按钮来改变state中的颜色

// Child2
class Child2 extends React.Component { shouldComponentUpdate() {
return true;
} render() {
return <Child3 />
}
}

增加shouldComponentUpdate来决定这个组件是否渲染。当我在shouldComponentUpdate中返回true的时候,一切都是那么地正常,但是当我返回false的时候,颜色将不再发生变化。

See the Pen react context problem by 糊一笑 (@rynxiao) on CodePen.

既然发生了这样的情况,那是否意味着我们不能再用context,没有绝对的事情,在这篇文章How to safely use React context中给出了一个解决方案,我们再将上面的例子改造一下:

// 重新定义一个发布对象,每当颜色变化的时候就会发布新的颜色信息
// 这样在订阅了颜色改变的子组件中就可以收到相关的颜色变化讯息了
class Theme {
constructor(color) {
this.color = color;
this.subscriptions = [];
} setColor(color) {
this.color = color;
this.subscriptions.forEach(f => f());
} subscribe(f) {
this.subscriptions.push(f)
}
} class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { theme: new Theme('red') };
this.changeColor = this.changeColor.bind(this)
} getChildContext() {
return { theme: this.state.theme }
} changeColor() {
this.state.theme.setColor('green');
} render() {
return (
<div className="test">
<button onClick={ this.changeColor }>change color to green</button>
<Child1 />
</div>
)
}
} const Child1 = () => {
return <Child2 />
} class Child2 extends React.Component { shouldComponentUpdate() {
return false;
} render() {
return <Child3 />
}
} // 子组件中订阅颜色改变的信息
// 调用forceUpdate强制自己重新渲染
class Child3 extends React.Component { componentDidMount() {
this.context.theme.subscribe(() => this.forceUpdate());
} render() {
return <div style={{ color: this.context.theme.color }}>Red</div>
}
} Parent.childContextTypes = {
theme: PropTypes.object
}; Child3.contextTypes = {
theme: PropTypes.object
}; ReactDOM.render(<Parent />, document.getElementById('container'));

看上面的例子,其实就是一个订阅发布者模式,一旦父组件颜色发生了改变,我就给子组件发送消息,强制调用子组件中的forceUpdate进行渲染。

See the Pen react context problem resolve by 糊一笑 (@rynxiao) on CodePen.

但在开发中,一般是不会推荐使用forceUpdate这个方法的,因为你改变的有时候并不是仅仅一个状态,但状态改变的数量只有一个,但是又会引起其他属性的渲染,这样会变得得不偿失。

另外基于此原理实现的有一个库: MobX,有兴趣的可以自己去了解。

总体建议是:能别用context就别用,一切需要在自己的掌控中才可以使用。

总结

这是自己在使用React时的一些总结,本意是朝着偷懒的方向上去了解context的,但是在使用的基础上,必须知道它使用的场景,这样才能够防范于未然。

关于React组件之间如何优雅地传值的探讨的更多相关文章

  1. React组件之间通过Props传值的技巧(小案例,帮助体会理解props、state、受控组件和非受控组件等)

    本文重要是根据react小书上的一个很简单的例子改编的,加上自己的学习理解,希望可以通过实际案例让大家对概念有更清晰的理解,当然也希望能一块学习. import React,{Component} f ...

  2. React 组件之间通信 All in One

    React 组件之间通信 All in One 组件间通信 1. 父子组件之间通信 props 2. 兄弟组件之间通信 3. 跨多层级的组件之间通信 Context API https://react ...

  3. react 组件之间传值

    谈及React时,就会想到一个很重要的思想,就是组件化思想.它将可以重用的部分进行组件化开发,形成一个个相对独立的组件,那么组件化后,你也会提出些疑问,组件与组件之间,将怎样进行信息的传递呢?下面来介 ...

  4. react组件之间传值方式

    1.父向子(通过props传值) 2.父向更深层的子(通过context传值) 3.子向父(通过回调函数传值:在父组件中创建一个函数来接收子组件传过来的参数值,通过父组件将这个函数做为子组件的属性传递 ...

  5. 使用reflux进行react组件之间的通信

    前言 组件之间为什么要通信?因为有依赖. 那么,作为React组件,怎么通信? React官网说, 进行 父-子 通信,可以直接pass props. 进行 子-父 通信,往父组件传给子组件的函数注入 ...

  6. react组件之间的几种通信情况

    组件之间的几种通信情况 父组件向子组件通信 子组件向父组件通信 跨级组件通信 没有嵌套关系组件之间的通信 1,父组件向子组件传递 React数据流动是单向的,父组件向子组件通信也是最常见的;父组件通过 ...

  7. react 组件之间的通信

    react推崇的是单向数据流,自上而下进行数据的传递,但是由下而上或者不在一条数据流上的组件之间的通信就会变的复杂.解决通信问题的方法很多,如果只是父子级关系,父级可以将一个回调函数当作属性传递给子级 ...

  8. react组件之间的组合方式

    组合方式: 1/直接嵌套的方式 2/组件以变量的形式放置 3/可以通过props值,以变量的形式相当于作为参数传递父组件,然后进行组合 import React,{Component} from 'r ...

  9. 关于react组件之间的通信

    才开始学react刚好到组件通信这一块,就简单的记录下组件间的通信方式:父到子:props.context,子到父:自定义事件.回调,兄弟组件:共父props传递.自定义事件import React, ...

随机推荐

  1. C#中结构体定义并转换字节数组

    最近的项目在做socket通信报文解析的时候,用到了结构体与字节数组的转换:由于客户端采用C++开发,服务端采用C#开发,所以双方必须保证各自定义结构体成员类型和长度一致才能保证报文解析的正确性,这一 ...

  2. css-子div设置margin-top影响父div

    父元素的第一个子元素的上边距margin-top如果碰不到有效的border或者padding.就会不断一层一层的找自己父元素,祖先元素,所有需要在父元素设置border,或者padding

  3. C#实现将输入的数自动转换为科学计数法

    一朋友写了一个把输入的整型或浮点数转换为科学计数法表示的算法,写好后叫我去帮他看看有没有什么bug之类的没有考虑周全.我还没有细看就已经把我吓到了----整整写了将近三百行代码.我也没说他什么,只是回 ...

  4. vue2.0 带头冲锋(先穿鞋)

    事先说明:这次截图纯手工敲打,可不容易了.刚学会站直,不穿鞋,不得直接摔个狗食屎.(皮糙肉厚也顶不住啊). 废话不多了 !开始学基础.学过anguler1.0 ,会比较容易学vue. 溶解使用的是 v ...

  5. 深入理解Postgres中的cache

    众所周知,缓存是提高数据库性能的一个重要手段.本文着重讲一讲PostgreSQL中的缓存相关的东西.当然万变不离其宗,原理都是共同的,理解了这些,你也很容易把它运用到其它数据库中. What is a ...

  6. 登山(Climb)

    题目: Rocky山脉有n个山峰,一字排开,从西向东依次编号为1, 2, 3, --, n.每个山峰的高度都是不一样的.编号为i的山峰高度为hi.  小修从西往东登山.每到一座山峰,她就回头观望自己走 ...

  7. linux如何在日志中查找关键字、前几行、结尾几行

    如何使用命令行快速查看项目日志是每个开发人员必备技能,尤其在没有专门日志搜集系统的情况下,想要知道目前项目运行状态最好的办法就是打开log日志一瞅即明白. 复杂的到用时再查不晚,但是简单的还是有必要掌 ...

  8. HTML Entity 字符实体

    目录 1. HTML Entity 2. 字符与Entity Name的互相转换 3. 字符与Entity Number的互相转换 1. HTML Entity 1.1 介绍 在编写HTML页面时,需 ...

  9. 【深圳】大湾区第三次.NET技术交流会(网络直播)

    活动简介 云.devops.微服务.容器是现在这个发展阶段的软件形态, 本次活动我们围绕这些话题介绍.NET生态下的发展.SQL Server 2017可根据需要运行本地部署的SQL Server,当 ...

  10. [搜索]ElasticSearch Java Api(一) -添加数据创建索引

    转载:http://blog.csdn.net/napoay/article/details/51707023 ElasticSearch JAVA API官网文档:https://www.elast ...