UI 组件和容器组件
- React-Redux 将所有组件分为两大类:UI ( presentational component ) 组件和容器 ( container component ) 组件
- UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。
- 如果一个组件既有 UI 又有业务逻辑则将其进行拆分:外面是一个容器组件,里面是一个 UI 组件,前者负责与外部的通信将数据传给后者,由后者渲染出视图。
- React-Redux 规定:所有 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说用户负责视图层, 状态管理则是全部交给它。
/*
* UI 组件
* 只负责 UI 的呈现,不带有任何业务逻辑
* 没有状态 --- 不使用 this.state 这个变量
* 所有数据由参数 this.props 提供
* 不使用任何 Redux 的 API
*/
const Titile = (value) => <h2>{ value }</h2>
/*
* 容器组件
* 负责管理数据和业务逻辑
* 带有内部状态
* 使用 Redux 的 API
*/
connect()
- 用于从 UI 组件生成容器组件
import { connect } from 'react-redux'
const VisibilityTodoList = connect({
mapStateToProps,
mapDispatchToProps
})(TodoList)
- 接收的两个参数 mapStateToProps 和 mapDispatchToProps 定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将 state 映射到 UI 组件的参数 props;后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。
mapStateToProps
- 建立一个从 ( 外部的 ) state 对象到 ( UI 组件的 ) props 对象的映射关系
- 它执行后返回一个对象,里面的每一个键值对就是一个映射,如下:
const mapStateToProps = state => ({
todos: getVisibleTodos(state.todos, state.visibilityFilter)
})
注解:如上 mapStateToProps 是一个函数,它接收 state 作为参数,返回一个对象。这个对象有一个 todos 属性,代表 UI 组件的同名参数,后面的 getVisibleTodos 函数根据 state 来计算出 todos 的值示例如下:
const getVisibleTodos = (todos, filter) => {
switch(filter) {
case VisibilityFilters.SHOW_ALL:
return todos
case VisibilityFilters.SHOW_COMPLETED:
return todos.filter(v => v.completed)
case VisibilityFilters.SHOW_ACTIVE:
return todos.filter(v => !v.completed)
default:
throw new Error(`Unknown filter: ${filter}`)
}
}
- mapStateToProps 会订阅 Store,每当 state 更新的时候,就会自动执行,重新来计算 UI 组件的参数,来触发 UI 组件的重新渲染。
- mapStateToProps还会接收第二个参数,代表容器组件的 props 对象:
const mapStateToProps = (state, ownProps) => ({
active: ownProps.filter === state.visibilityFilter
})
- 使用 ownProps 作为参数后,如果容器组件的参数发生变化也会引起 UI 组件的重新渲染
- connect 方法可以省略 mapStateToProps 参数,这样的话 UI 组件就不会订阅 Store,那么 Store 的更新就不会引起 UI 组件的更新
mapDispatchToProps
- 它是 connect 的第二个参数,用来建立 UI 组件参数到 store.dispatch 方法的映射,它定义了哪些用户的操作应该当做 Action 传给 Store,它可以是一个函数也可以是一个对象
// 1.作为函数,接收 dispatch,ownPros ( 容器组件的 props 对象 ) 两个参数。
// 作为函数,返回一个对象,对象的每个键值对都是一个映射,定义了 UI 组件的参数应该怎样发出 Action
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: ownProps.filter
})
}
}
}
// 2.作为对象,键名也是对应 UI 组件的同名参数
// 键值是一个函数被当做 Action creator 返回的 action 由 Redux 自动发出
const mapDispatchToProps = {
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
}
}
Provider 组件
- connect 方法生成容器组件以后,需要让容器组件拿到 state 对象,才能生成 UI 组件的参数
- React-Redux 提供 Provider 组件可以让容器组件拿到 state
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './components/App'
import rootReducer from './reducers'
const store = createStore(rootReducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
如上:Provider 在根组件外面包了一层,这样 App 所有子组件都能默认拿到 state 了
文章内容学习自阮一峰老师的:Redux 入门教程(三):React-Redux 的用法
网友评论