• 展示组件和容器组件
    • 问题描述
    • 解决办法.
    • 容器组件
    • UI组件
  • 参考资料:

    展示组件和容器组件

    问题描述

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

    1. class Clock extends React.Component {
    2. constructor(props) {
    3. super(props);
    4. this.state = {time: this.props.time};
    5. this._update = this._updateTime.bind(this);
    6. }
    7. render() {
    8. var time = this._formatTime(this.state.time);
    9. return (
    10. <h1>{ time.hours } : { time.minutes } : { time.seconds }</h1>
    11. );
    12. }
    13. componentDidMount() {
    14. this._interval = setInterval(this._update, 1000);
    15. }
    16. componentWillUnmount() {
    17. clearInterval(this._interval);
    18. }
    19. _formatTime(time) {
    20. var [ hours, minutes, seconds ] = [
    21. time.getHours(),
    22. time.getMinutes(),
    23. time.getSeconds()
    24. ].map(num => num < 10 ? '0' + num : num);
    25. return {hours, minutes, seconds};
    26. }
    27. _updateTime() {
    28. this.setState({time: new Date(this.state.time.getTime() + 1000)});
    29. }
    30. }
    31. ReactDOM.render(<Clock time={ new Date() }/>, ...);

    解决办法.

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

    容器组件

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

    1. // Clock/index.js
    2. import Clock from './Clock.jsx'; // <-- Clock是一个UI组件
    3. export default class ClockContainer extends React.Component {
    4. constructor(props) {
    5. super(props);
    6. this.state = {time: props.time};
    7. this._update = this._updateTime.bind(this);
    8. }
    9. render() {
    10. return <Clock { ...this._extract(this.state.time) }/>;
    11. }
    12. componentDidMount() {
    13. this._interval = setInterval(this._update, 1000);
    14. }
    15. componentWillUnmount() {
    16. clearInterval(this._interval);
    17. }
    18. _extract(time) {
    19. return {
    20. hours: time.getHours(),
    21. minutes: time.getMinutes(),
    22. seconds: time.getSeconds()
    23. };
    24. }
    25. _updateTime() {
    26. this.setState({time: new Date(this.state.time.getTime() + 1000)});
    27. }
    28. };

    UI组件

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

    1. // Clock/Clock.jsx
    2. export default function Clock(props) {
    3. var [ hours, minutes, seconds ] = [
    4. props.hours,
    5. props.minutes,
    6. props.seconds
    7. ].map(num => num < 10 ? '0' + num : num);
    8. return <h1>{ hours } : { minutes } : { seconds }</h1>;
    9. };

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

    1. import Clock from './Clock.jsx';
    2. export default class ClockContainer extends React.Component {
    3. render() {
    4. return <Clock />;
    5. }
    6. }

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

    1. export default function (Component) {
    2. return class Container extends React.Component {
    3. render() {
    4. return <Component />;
    5. }
    6. }
    7. }

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

    参考资料:

    • https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.mbglcakmp
    • https://github.com/krasimir/react-in-patterns/tree/master/patterns/presentational-and-container
    • https://medium.com/@learnreact/container-components-c0e67432e005