展示组件和容器组件

问题描述

UI和业务逻辑和数据混杂在一起.

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {time: this.props.time};
    this._update = this._updateTime.bind(this);
  }

  render() {
    var time = this._formatTime(this.state.time);
    return (
      <h1>{ time.hours } : { time.minutes } : { time.seconds }</h1>
    );
  }

  componentDidMount() {
    this._interval = setInterval(this._update, 1000);
  }

  componentWillUnmount() {
    clearInterval(this._interval);
  }

  _formatTime(time) {
    var [ hours, minutes, seconds ] = [
      time.getHours(),
      time.getMinutes(),
      time.getSeconds()
    ].map(num => num < 10 ? '0' + num : num);

    return {hours, minutes, seconds};
  }

  _updateTime() {
    this.setState({time: new Date(this.state.time.getTime() + 1000)});
  }
}

ReactDOM.render(<Clock time={ new Date() }/>, ...);

解决办法.

我们将组件拆分成容器(container)组件和UI(presentation)组件

容器组件

容器组件关心数据(包括数据的格式和数据的来源等). 容器组件关心具体的业务逻辑, 它接收数据并将数据整理成我们的UI组件需要的格式传递给UI组件. 我们常使用高阶组件去建立容器组件. 一般情况下, 容器组件的render方法里面包含的只会是UI组件.

// Clock/index.js
import Clock from './Clock.jsx'; // <-- Clock是一个UI组件

export default class ClockContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {time: props.time};
    this._update = this._updateTime.bind(this);
  }

  render() {
    return <Clock { ...this._extract(this.state.time) }/>;
  }

  componentDidMount() {
    this._interval = setInterval(this._update, 1000);
  }

  componentWillUnmount() {
    clearInterval(this._interval);
  }

  _extract(time) {
    return {
      hours: time.getHours(),
      minutes: time.getMinutes(),
      seconds: time.getSeconds()
    };
  }

  _updateTime() {
    this.setState({time: new Date(this.state.time.getTime() + 1000)});
  }
};

UI组件

UI组件关心组件展示出来是什么样子. UI组件一般由基本的html标签为基础, 用以在页面上展示. 理想的UI组件应该被设计为没有外部依赖的组件. 常用的实现是使用没有内部state的无状态组件(stateless function)

// Clock/Clock.jsx
export default function Clock(props) {
  var [ hours, minutes, seconds ] = [
    props.hours,
    props.minutes,
    props.seconds
  ].map(num => num < 10 ? '0' + num : num);

  return <h1>{ hours } : { minutes } : { seconds }</h1>;
};

容器组件封装了封装了业务逻辑, 并且可以灵活的讲不同的数据注入不同的UI组件中, 这是使用容器组件带来明显的好处. 常见使用容器组件的方法是我们不去在容器组件内部规定哪个UI组件将被渲染, 而是建立一个接收一个UI组件的函数, 去灵活的让我们的容器组件可以包裹任意UI组件. 比如, 和使用下面的形式相比:

import Clock from './Clock.jsx';
export default class ClockContainer extends React.Component {
  render() {
    return <Clock />;
  }
}

我们更推荐下面这种写法:

export default function (Component) {
  return class Container extends React.Component {
    render() {
      return <Component />;
    }
  }
}

使用这种方法,我们的容器就编程了能渲染任意UI组件的容器,非常的灵活. 回到时钟的例子, 如果这时候你想把数显时钟变成一个指针时钟, 只需要替换容器组件内部的UI时钟组件就可以了.

参考资料:

results matching ""

    No results matching ""