关于React组件之间如何优雅地传值的探讨
2017-12-25 15:16
656 查看
闲话不多说,开篇撸代码,你可以会看到类似如下的结构:
See the Pen react props by 糊一笑 (@rynxiao) on CodePen.
当一个组件嵌套了若干层子组件时,而想要在特定的组件中取得父组件的属性,就不得不将
那么
当然这只是一种非常简单的形式解析,
这样当在
这只是一个任一组件的大致演示,这就意味着你可以在任何组件中来改变
但是,好用的东西往往也有副作用,官方也给出了几点不要使用
如果你想你的应用处于稳定状态,不要用
如果你不太熟悉
如果你不是一个资深的
鉴于以上三种情况,官方更好的建议是老老实实使用
下面主要大致讲一下
可以看到,在子组件中,所有的
See the Pen react context by 糊一笑 (@rynxiao) on CodePen.
这样做貌似十分简单,但是你可能会遇到这样的问题:当改变了
增加一个按钮来改变
增加
See the Pen react context problem by 糊一笑 (@rynxiao) on CodePen.
既然发生了这样的情况,那是否意味着我们不能再用
看上面的例子,其实就是一个订阅发布者模式,一旦父组件颜色发生了改变,我就给子组件发送消息,强制调用子组件中的
See the Pen react context problem resolve by 糊一笑 (@rynxiao) on CodePen.
但在开发中,一般是不会推荐使用
另外基于此原理实现的有一个库: MobX,有兴趣的可以自己去了解。
总体建议是:能别用
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组件之间如何优雅地传值的探讨
- 如何优雅的设计 React 组件
- React 组件之间如何交流
- 关于在reactjs项目中如何用webpack配置组件按需加载
- 如何优雅的设计 React 组件
- React 组件之间如何交流
- React 组件之间如何交流
- react组件之间如何交流
- React 组件之间如何交流
- React 组件之间如何交流
- 如何优雅的设计React组件
- 关于react组件之间的通信
- 关于如何减缓、解除hibernate与domain之间的强关系限制以及与mybatis简单对比
- 关于如何在testlink里使用fckedit组件来上传图片
- 详解Angular2组件之间如何通信
- Android--(1)Activity组件的作用,如何创建Activity,如何启动Activity,以及Activity之间跳转的示例
- 关于React的组件优化笔记
- 关于react中组件通信的几种方式详解
- 如何开发可重用的React组件
- 跟大牛之间关于hibernate的一些探讨记录