使用React我们首先要知道如何传递数据,组件如何沟通,才能展示我们想要的数据。下面的列子都是使用ES6语法,不懂的同学需要先学习ES6语法。
数据流
React是单向数据流,从父节点传递到子节点(通过props
)。如果顶层的某个props
改变了,React会重渲染所有的子节点(未做性能优化)。严格意义上React只提供,也强烈建议使用这种数据交流方式。
Props
props
是property的缩写,可以理解为HTML标签的attribute。请把props
当做只读的(不可以使用this.props
直接修改props),props
是用于整个组件树中传递数据和配置。在当前组件访问props
,使用this.props
。在什么情况下可以使用props
,请看组件生命周期
1 2 3 4 5 6 7 8 9 10 11 class Component { constructor (props ){ super (props); } render ( ){ return ( <div title ={this.props.title} > </div > ) } } <Component title="test" />
PropTypes
PropsTypes
是React中用来定义props
的类型,不符合定义好的类型会报错。建议可复用组件要使用prop验证!接着上面的列子设置PropsTypes
如下:
1 2 3 4 5 6 class Component { ... } Component .PropsType = { title : React .PropTypes .string , }
React.PropTypes
提供很多验证器 (validator) 来验证传入数据的有效性。官方定义的验证器如下,不是使用ES6语法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 React .createClass ({ propTypes : { optionalArray : React .PropTypes .array , optionalBool : React .PropTypes .bool , optionalFunc : React .PropTypes .func , optionalNumber : React .PropTypes .number , optionalObject : React .PropTypes .object , optionalString : React .PropTypes .string , optionalSymbol : React .PropTypes .symbol , optionalNode : React .PropTypes .node , optionalElement : React .PropTypes .element , optionalMessage : React .PropTypes .instanceOf (Message ), optionalEnum : React .PropTypes .oneOf (['News' , 'Photos' ]), optionalUnion : React .PropTypes .oneOfType ([ React .PropTypes .string , React .PropTypes .number , React .PropTypes .instanceOf (Message ) ]), optionalArrayOf : React .PropTypes .arrayOf (React .PropTypes .number ), optionalObjectOf : React .PropTypes .objectOf (React .PropTypes .number ), optionalObjectWithShape : React .PropTypes .shape ({ color : React .PropTypes .string , fontSize : React .PropTypes .number }), requiredFunc : React .PropTypes .func .isRequired , requiredAny : React .PropTypes .any .isRequired , customProp : function (props, propName, componentName ) { if (!/matchme/ .test (props[propName])) { return new Error ('Validation failed!' ); } } }, });
defaultProps
如何设置组件默认的props
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var Component = React .createClass ({ getDefaultProps ( ){ return { } } }) class Component { ... } Component .defaultProps = {}class Component { static defaultProps = { } ... }
state
每个组件都有属于自己的state
,state
和props
的区别在于前者之只存在于组件内部,只能从当前组件调用this.setState
修改state值(不可以直接修改this.state
)。一般我们更新子组件都是通过改变state
值,更新新子组件的props
值从而达到更新。
那如何设置默认state?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var Component = React .createClass ({ getInitialState ( ){ return { } } }) class Component { constructor ( ){ this .state = {} } ... }
props和state使用方式
尽可能使用props
当做数据源,state
用来存放状态值(简单的数据),如复选框、下拉菜单等。
组件沟通
组件沟通因为React的单向数据流方式会有所限制,下面述说组件之间的沟通方式。
父子组件沟通
这种方式是最常见的,也是最简单的。
父组件更新子组件状态,通过传递props
,就可以了。
这种情况需要父组件传递回调函数给子组件,子组件调用触发即可。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class Child extends React.Component { constructor (props ){ super (props); this .state = {} } render ( ){ return ( <div > {this.props.text} <br /> <button onClick ={this.props.refreshParent} > 更新父组件 </button > </div > ) } } class Parent extends React.Component { constructor (props ){ super (props); this .state = {} } refreshChild ( ){ return (e )=> { this .setState ({ childText : "父组件沟通子组件成功" , }) } } refreshParent ( ){ this .setState ({ parentText : "子组件沟通父组件成功" , }) } render ( ){ return ( <div > <h1 > 父子组件沟通</h1 > <button onClick ={this.refreshChild()} > 更新子组件 </button > <Child text ={this.state.childText || "子组件未更新 "} refreshParent ={this.refreshParent.bind(this)} /> {this.state.parentText || "父组件未更新"} </div > ) } }
codepen例子React组件之父子组件沟通 。
兄弟组件沟通
当两个组件有相同的父组件时,就称为兄弟组件(堂兄也算的)。按照React单向数据流方式,我们需要借助父组件进行传递,通过父组件回调函数改变兄弟组件的props
。
方式一
通过props
传递父组件回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Brother1 extends React.Component { constructor (props ){ super (props); this .state = {} } render ( ){ return ( <div > <button onClick ={this.props.refresh} > 更新兄弟组件 </button > </div > ) } } class Brother2 extends React.Component { constructor (props ){ super (props); this .state = {} } render ( ){ return ( <div > {this.props.text || "兄弟组件未更新"} </div > ) } } class Parent extends React.Component { constructor (props ){ super (props); this .state = {} } refresh ( ){ return (e )=> { this .setState ({ text : "兄弟组件沟通成功" , }) } } render ( ){ return ( <div > <h2 > 兄弟组件沟通</h2 > <Brother1 refresh ={this.refresh()}/ > <Brother2 text ={this.state.text}/ > </div > ) } }
codepen例子:React组件之兄弟组件沟通 。
方式二
但是如果组件层次太深(如下图),上面的兄弟组件沟通方式就效率低了(不建议组件层次太深)。
React提供了一种上下文方式(挺方便的),可以让子组件直接访问祖先的数据或函数,无需从祖先组件一层层地传递数据到子组件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 class Brother1 extends React.Component { constructor (props ){ super (props); this .state = {} } render ( ){ return ( <div > <button onClick ={this.context.refresh} > 更新兄弟组件 </button > </div > ) } } Brother1 .contextTypes = { refresh : React .PropTypes .any } class Brother2 extends React.Component { constructor (props ){ super (props); this .state = {} } render ( ){ return ( <div > {this.context.text || "兄弟组件未更新"} </div > ) } } Brother2 .contextTypes = { text : React .PropTypes .any } class Parent extends React.Component { constructor (props ){ super (props); this .state = {} } getChildContext ( ){ return { refresh : this .refresh (), text : this .state .text , } } refresh ( ){ return (e )=> { this .setState ({ text : "兄弟组件沟通成功" , }) } } render ( ){ return ( <div > <h2 > 兄弟组件沟通</h2 > <Brother1 /> <Brother2 text ={this.state.text}/ > </div > ) } } Parent .childContextTypes = { refresh : React .PropTypes .any , text : React .PropTypes .any , }
codepen例子:React组件之兄弟组件沟通2
全局事件
For communication between two components that don’t have a parent-child relationship, you can set up your own global event system. Subscribe to events in componentDidMount()
, unsubscribe in componentWillUnmount()
, and call setState()
when you receive an event.Flux pattern is one of the possible ways to arrange this.
官网中提到可以使用全局事件来进行组件间的通信,官网推荐Flux(Facebook官方出的),还有Relay、Redux、trandux等第三方类库。这些框架思想都一致,都是统一管理组件state变化情况,达到数据可控 目的。本人使用了Redux,建议要会其中一种。对于EventEmitter或PostalJS这类的第三方库是不建议使用的,这类全局事件框架并没有统一管理组件数据变化,用多了会导致数据流不可控。
这里就不细说,请选择其中一种类库,深入学习下。
总结
简单的组件交流我们可以使用上面非全局事件的简单方式,但是当项目复杂,组件间层次越来越深,上面的交流方式就不太合适(当然还是要用到的,简单的交流)。强烈建议使用Flux、Relay、Redux、trandux等类库其中一种,这些类库不只适合React,像Angular等都可以使用。