React(5)-组件协同使用

一、组件协同使用

目的:

  1. 逻辑清晰
  2. 代码模块化
  3. 封装细节
  4. 代码的复用

方法:嵌套和mixin

1.组件嵌套

父组件通过this.props把属性传递给子组件,子组件通过this.refs委托方法和属性给父组件。

举个栗子

import React from 'react';
import { render } from 'react-dom';

const ProfilePic = (props) => {
  return (
    <img src={'http://graph.facebook.com/' + props.username + '/picture'} />
  );
}

const ProfileLink = (props) => {
  return (
    <a href={'http://www.facebook.com/' + props.username}>
      {props.username}
    </a>
  );
}

const Avatar = (props) => {
  return (
    <div>
      <ProfilePic username={props.username} />
      <ProfileLink username={props.username} />
    </div>
  );
}

render(
  <Avatar username="pwh" />,
  document.getElementById('example')
);

2.Mixins

简单的讲mixin就是抽离可以共用的行为,在需要时透过 mixins 属性设定给组件,提高代码的复用率。

举个官方的栗子:


var IntervalMixin = {
    setInterval:function(callback,intercal){
       var token = setInterval(callback,intercal);
       this.__intervals.push(token);
       return token;
    },
    componentDidMount:function(){
       this.__intervals = []
    },
    componentWillUnmount = function(){
       this.__intervals.map(clearInterval);
    }
};
var SetIntervalMixin = {
  componentWillMount: function() {
    this.intervals = [];
  },
  setInterval: function() {
    this.intervals.push(setInterval.apply(null, arguments));
  },
  componentWillUnmount: function() {
    this.intervals.map(clearInterval);
  }
};

var TickTock = React.createClass({
  mixins: [SetIntervalMixin], // Use the mixin
  getInitialState: function() {
    return {seconds: 0};
  },
  componentDidMount: function() {
    this.setInterval(this.tick, 1000); // Call a method on the mixin
  },
  tick: function() {
    this.setState({seconds: this.state.seconds + 1});
  },
  render: function() {
    return (
      <p>
        React has been running for {this.state.seconds} seconds.
      </p>
    );
  }
});

React.render(
  <TickTock />,
  document.getElementById('example')
);

React自带的Mixins(react-with-addons.js)


React.addons = {
       TransitionGroup: ReactTransitionGroup,
       CSSTransitionGroup: ReactCSSTransitionGroup,
       //用于处理动画和过渡,这些通常实现起来都不简单,例如在一个组件移除之前执行一段动画。
       LinkedStateMixin: LinkedStateMixin,//用于简化用户表单输入数据和组件 state 之间的双向数据绑定。
       classSet:classSet//用于更加干净简洁地操作 DOM 中的 class 字符串。
       PureRenderMixin: ReactComponentWithPureRenderMixin,//在某些场景下的性能检测器。
       batchedUpdates: function () {
             if ("development" !== 'production') {
                      "development" !== 'production' ? warning(warnedAboutBatchedUpdates, 'React.addons.batchedUpdates is deprecated. Use ' + 'ReactDOM.unstable_batchedUpdates instead.') : undefined;
                      warnedAboutBatchedUpdates = true;
            }
            return ReactUpdates.batchedUpdates.apply(this, arguments);
       },
       cloneWithProps: cloneWithProps,//用于实现 React 组件浅复制,同时改变它们的 props 。
       createFragment: ReactFragment.create,
       shallowCompare: shallowCompare,
       update: update//一个辅助方法,使得在 JavaScript 中处理不可变数据更加容易。
};

二、组件循环插入子元素的方法

如果组件中包含通过循环插入的子元素,为了保证重新渲染 UI 的时候能够正确显示这些子元素,每个元素都需要通过一个特殊的 key 属性指定一个唯一值。

//1.
const ListItemWrapper = (props) => <li>{props.data.text}</li>;
//2.
const MyComponent = (props) => {
  var items = {};
  this.props.results.forEach((result) => {
    items['result-' + result.id] = <li>{result.text}</li>;
  });
  return (
    <ol>
      {items}
    </ol>
   );
}

//3.
render: function() {
 var results = this.props.results; 
return (<ol> 
       {results.map(function(result) {
          return <li key={result.id}>{result.text}</li>; 
       })} 
   </ol>);
 }
//4.
var ListItemWrapper = React.createClass({
  render: function() {
    return <li>{this.props.data.text}</li>;
  }
});
var MyComponent = React.createClass({
  render: function() {
    return (
      <ul>
        {this.props.results.map(function(result) {
           return <ListItemWrapper key={result.id} data={result}/>;
        })}
      </ul>
    );
  }
});

this.props.children
组件标签里面包含的子元素会通过 props.children 传递进来。

一般来说,可以直接将这个属性作为父组件的子元素 render:

const Parent = (props) => <div>{props.children}</div>;

props.children 通常是一个组件对象的数组,但是当只有一个子元素的时候,props.children 将是这个唯一的子元素,而不是数组了。

React.Children 提供了额外的方法方便操作这个属性。

  1. React.Children.map:array React.Children.map(object children, function fn [, object thisArg])
  2. React.Children.forEach:React.Children.forEach(object children, function fn [, object thisArg])
  3. React.Children.count:number React.Children.count(object children)
  4. React.Children.only:object React.Children.only(object children)
  5. React.Children.toArray:array React.Children.toArray(object children)

三、侵入式插件

查看实例:


var SuperSelect = React.createClass({
     render:function(){
         return;
     },
     //挂载插件
     componentDidMount:function(){
         var el = this.el = document.createElement('div');
         this.getDOMNode().appendChild(el);
         $(el).superSelect(this.props);
         $(el).on('superSelect',this.handleSuperSelectChange);
     },
     handleSuperSelectChange:function(){
         //dosomething
     },
     //卸载插件
     componentWillUnmount:function(){
         this.getDOMNode().removeChild(this.el);
         $(this.el).off();
     }
})

下面有两种重新装卸插件的方式:
1.简单可靠

componentDidUpdate:function(){
    this.componentWillUnmount();
    this.componentDidMount();
}

2.高效,清晰

componentWillReceiveProps:function(){
    $(this.el).superSelect("update",nextProps);
}