使用React我们首先要知道如何传递数据,组件如何沟通,才能展示我们想要的数据。下面的列子都是使用ES6语法,不懂的同学需要先学习ES6语法。
数据流
React是单向数据流,从父节点传递到子节点(通过props
)。如果顶层的某个props
改变了,React会重渲染所有的子节点(未做性能优化)。严格意义上React只提供,也强烈建议使用这种数据交流方式。
Props
props
是property的缩写,可以理解为HTML标签的attribute。请把props
当做只读的(不可以使用this.props
直接修改props),props
是用于整个组件树中传递数据和配置。在当前组件访问props
,使用this.props
。在什么情况下可以使用props
,请看组件生命周期
class Component {
constructor(props){
super(props);
}
render(){
return (
)
}
}
//调用title就传进去了
PropTypes
PropsTypes
是React中用来定义props
的类型,不符合定义好的类型会报错。建议可复用组件要使用prop验证!接着上面的列子设置PropsTypes
如下:
class Component {
...
}
Component.PropsType = {
title: React.PropTypes.string,
}
React.PropTypes
提供很多验证器 (validator) 来验证传入数据的有效性。官方定义的验证器如下,不是使用ES6语法。
React.createClass({
propTypes: {
// 可以声明 prop 为指定的 JS 基本类型。默认
// 情况下,这些 prop 都是可传可不传的。
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,
// 所有可以被渲染的对象:数字,
// 字符串,DOM 元素或包含这些类型的数组(or fragment) 。
optionalNode: React.PropTypes.node,
// React 元素
optionalElement: React.PropTypes.element,
// 你同样可以断言一个 prop 是一个类的实例。
// 用 JS 的 instanceof 操作符声明 prop 为类的实例。
optionalMessage: React.PropTypes.instanceOf(Message),
// 你可以用 enum 的方式
// 确保你的 prop 被限定为指定值。
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
}),
// 你可以在任意东西后面加上 `isRequired`
// 来确保 如果 prop 没有提供 就会显示一个警告。
requiredFunc: React.PropTypes.func.isRequired,
// 不可空的任意类型
requiredAny: React.PropTypes.any.isRequired,
// 你可以自定义一个验证器。如果验证失败需要返回一个 Error 对象。
// 不要直接使用 `console.warn` 或抛异常,
// 因为这在 `oneOfType` 里不起作用。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
},
/* ... */
});
defaultProps
如何设置组件默认的props
?
//React提供的crateClass创建方式
var Component = React.createClass({
getDefaultProps(){
return {
//这里设置defaultProps
}
}
})
//ES6
class Component {
...
}
Component.defaultProps = {}
//ES7 stage-0
class Component {
static defaultProps = {
}
...
}
state
每个组件都有属于自己的state
,state
和props
的区别在于前者之只存在于组件内部,只能从当前组件调用this.setState
修改state值(不可以直接修改this.state
)。一般我们更新子组件都是通过改变state
值,更新新子组件的props
值从而达到更新。
那如何设置默认state?
//React提供的crateClass创建方式
var Component = React.createClass({
getInitialState(){
return {
//这里设置初始state值
}
}
})
//ES6 && ES7
class Component {
constructor(){
this.state = {}//在ES6中的构造函数中初始化,可以之直接赋值,在其他方法中,只能使用this.setState
}
...
}
props和state使用方式
尽可能使用props
当做数据源,state
用来存放状态值(简单的数据),如复选框、下拉菜单等。
组件沟通
组件沟通因为React的单向数据流方式会有所限制,下面述说组件之间的沟通方式。
父子组件沟通
这种方式是最常见的,也是最简单的。
父组件更新组件状态
父组件更新子组件状态,通过传递props
,就可以了。
子组件更新父组件状态
这种情况需要父组件传递回调函数给子组件,子组件调用触发即可。
代码示例:
class Child extends React.Component{
constructor(props){
super(props);
this.state = {}
}
render(){
return (
{this.props.text}
)
}
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {}
}
refreshChild(){
return (e)=>{
this.setState({
childText: "父组件沟通子组件成功",
})
}
}
refreshParent(){
this.setState({
parentText: "子组件沟通父组件成功",
})
}
render(){
return (
父子组件沟通
{this.state.parentText || "父组件未更新"}
)
}
}
codepen例子React组件之父子组件沟通 。
兄弟组件沟通
当两个组件有相同的父组件时,就称为兄弟组件(堂兄也算的)。按照React单向数据流方式,我们需要借助父组件进行传递,通过父组件回调函数改变兄弟组件的props
。
方式一
通过props
传递父组件回调函数。
class Brother1 extends React.Component{
constructor(props){
super(props);
this.state = {}
}
render(){
return (
)
}
}
class Brother2 extends React.Component{
constructor(props){
super(props);
this.state = {}
}
render(){
return (
{this.props.text || "兄弟组件未更新"}
)
}
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {}
}
refresh(){
return (e)=>{
this.setState({
text: "兄弟组件沟通成功",
})
}
}
render(){
return (
兄弟组件沟通
)
}
}
codepen例子:React组件之兄弟组件沟通。
方式二
但是如果组件层次太深(如下图),上面的兄弟组件沟通方式就效率低了(不建议组件层次太深)。
React提供了一种上下文方式(挺方便的),可以让子组件直接访问祖先的数据或函数,无需从祖先组件一层层地传递数据到子组件中。
class Brother1 extends React.Component{
constructor(props){
super(props);
this.state = {}
}
render(){
return (
)
}
}
Brother1.contextTypes = {
refresh: React.PropTypes.any
}
class Brother2 extends React.Component{
constructor(props){
super(props);
this.state = {}
}
render(){
return (
{this.context.text || "兄弟组件未更新"}
)
}
}
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 (
兄弟组件沟通
)
}
}
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 incomponentWillUnmount()
, and callsetState()
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等都可以使用。
参考文章
ReactJS组件间沟通的一些方法
React 数据流管理架构之 Redux 介绍