Container与Components 3个月前

前端开发
270
Container与Components

前言

在聊完了 React 和 Redux 整合后我们来谈谈分离 Presentational 和 Container Component 的概念,若你是第一次听过这个名词,我建议你可以先看看 Redux 作者 Dan AbramovFollow 所写的这篇文章 Presentational and Container Components

Container 与 Presentational Components 超级比一比

以下先参考 Redux 官网 列出两者相异之处:

  1. Presentational Components
    • 用途:怎么看事情(Markup、外观)
    • 是否让 Redux 意识到:否
    • 取得资料方式:从 props 取得
    • 改变资料方式:从 props 去呼叫 callback function
  • 写入方式:手动处理
  1. Container Components
  • 用途:怎么做事情(撷取资料,更新 State)

  • 是否让 Redux 意识到:是

  • 取得资料方式:订阅 Redux State(store)

  • 改变资料方式:Dispatch Redux Action

  • 写入方式:从 React Redux 产生

    从上面的分析读者可以发现,两者最大的差别在于 Component 主要负责单纯的 UI 的渲染,而 Container 则负责和 Redux 的 store 沟通,作为 ReduxComponent 之间的桥梁。这样的分法可以让程式架构和职责更清楚,所以接下来我们就使用上一章节的 Redux TodoApp 进行改造,改造成 Container 与 Presentational Components 模式。

Container Components

以下是 src/containers/TodoHeaderContainer/TodoHeaderContainer.js 的部份:

import { connect } from 'react-redux';
import TodoHeader from '../../components/TodoHeader';

// 将欲使用的 actions 引入
import {
  changeText,
  createTodo,
} from '../../actions';

const mapStateToProps = (state) => ({
  // 从 store 取得 todo state
  todo: state.getIn(['todo', 'todo'])
});

const mapDispatchToProps = (dispatch) => ({
  // 当使用者在 input 输入资料值即会触发这个函数,发出 changeText action 并附上使用者输入内容 event.target.value
  onChangeText: (event) => (
    dispatch(changeText({ text: event.target.value }))
  ),
  // 当使用者按下送出时,发出 createTodo action 并清空 input 
  onCreateTodo: () => {
    dispatch(createTodo());
    dispatch(changeText({ text: '' }));
  }
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(TodoHeader);

以下是 src/containers/TodoListContainer/TodoListContainer.js 的部份:

import { connect } from 'react-redux';
import TodoList from '../../components/TodoList';

import {
  deleteTodo,
} from '../../actions';

const mapStateToProps = (state) => ({
  todos: state.getIn(['todo', 'todos'])
});

const mapDispatchToProps = (dispatch) => ({
  onDeleteTodo: (index) => () => (
    dispatch(deleteTodo({ index }))
  )
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(TodoList);

Presentational Components

以下是 src/components/TodoHeader/TodoHeader.js 的部份:

import React from 'react';
import ReactDOM from 'react-dom';

// 开始建设 Component 并使用 connect 进来的 props 并绑定事件(onChange、onClick)。注意我们的 state 因为是使用 `ImmutableJS` 所以要用 `get()` 取值

const TodoHeader = ({
  onChangeText,
  onCreateTodo,
  todo,
}) => (
  <div>
    <h1>TodoHeader</h1>
    <input type="text" value={todo.get('text')} onChange={onChangeText} />
    <button onClick={onCreateTodo}>送出</button>
  </div>
);

export default TodoHeader;

以下是 src/components/TodoList/TodoList.js 的部份:

import React from 'react';
import ReactDOM from 'react-dom';

// Component 部分值的注意的是 todos state 是透过 map function 去迭代出元素,由于要让 React JSX 可以渲染并保持传入触发 event state 的 immutable,所以需使用 toJS() 转换 component of array。
// 由 Component 传进欲删除元素的 index

const TodoList = ({
  todos,
  onDeleteTodo,
}) => (
  <div>
    <ul>
    {
      todos.map((todo, index) => (
        <li key={index}>
          {todo.get('text')}
          <button onClick={onDeleteTodo(index)}>X</button>
        </li>
      )).toJS()
    }
    </ul>
  </div>
);

export default TodoList;

总结

That's it!透过区分 Container 与 Presentational Components 可以让程式架构和职责更清楚了!接下来我们将运用我们所学实际开发两个贴近生活的专案,让读者更加熟悉 React 生态系如何应用于实务上。

延伸阅读

  1. Presentational and Container Components
  2. Redux Usage with React
  3. React Higher Order Components in depth
  4. React higher order components
image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2223790
累计阅读

热门教程文档

C
14小节
Python
76小节
Next
43小节
Redis
14小节
Spring Cloud
8小节