React 组件数据流 && 组件间沟通

使用React咱们首先要知道如何传递数据,组件如何沟通,才能展现咱们想要的数据。下面的列子都是使用ES6语法,不懂的同窗须要先学习ES6语法。node

数据流

React是单向数据流,从父节点传递到子节点(经过props)。若是顶层的某个props改变了,React会重渲染全部的子节点(未作性能优化)。严格意义上React只提供,也强烈建议使用这种数据交流方式。react

Props

props是property的缩写,能够理解为HTML标签的attribute。请把props当作只读的(不可使用this.props直接修改props),props是用于整个组件树中传递数据和配置。在当前组件访问props,使用this.props。在什么状况下可使用props,请看组件生命周期git

class Component {
  constructor(props){
    super(props);
  }
  render(){
    return (
        <div title={this.props.title}></div>
    )
  }
}
<Component title="test"/>//调用title就传进去了

PropTypes

PropsTypes是React中用来定义props的类型,不符合定义好的类型会报错。建议可复用组件要使用prop验证!接着上面的列子设置PropsTypes以下:github

class Component {
  ...
}
Component.PropsType = {
  title: React.PropTypes.string,
}

React.PropTypes 提供不少验证器 (validator) 来验证传入数据的有效性。官方定义的验证器以下,不是使用ES6语法。redux

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

如何设置组件默认的propssegmentfault

//React提供的crateClass建立方式
var Component = React.createClass({
  getDefaultProps(){
    return {
      //这里设置defaultProps
    }
  }
})
//ES6
class Component {
  ...
}
Component.defaultProps = {}
//ES7 stage-0
class Component {
  static defaultProps = {
    
  }
  ...
}

state

每一个组件都有属于本身的statestateprops的区别在于前者之只存在于组件内部,只能从当前组件调用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 (
      <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传递父组件回调函数。

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提供了一种上下文方式(挺方便的),可让子组件直接访问祖先的数据或函数,无需从祖先组件一层层地传递数据到子组件中。

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等均可以使用。

参考文章

相关文章
相关标签/搜索