与 React 整合
Redux 与 React 的整合主要通过react-redux
这个绑定库。并且主要的实现目的就是将 React 组件与 Redux 关联起来。但是需要注意的是,在 React 中使用 Redux,React-Redux 这个绑定库并不是必需的,而且使用这个库需要掌握额外的 API,并要遵循其组件拆分规范。
根据 Redux 作者的建议,只有遇到 React 解决不了的问题,才会需要 Redux。在大多数情况下,React 已经足够,所以对于在项目中使用 Redux,要根据项目需要仔细甄别。按照一般经验来说,在以下场景内会用到 Redux。
- 用户的使用方式复杂。
- 不同的身份的用户有不同的使用方式,例如普通用户与管理员。
- 多个用户之间需要协作。
- 与服务器有大量的交互,或者使用了 WebSocket。
- UI 视图需要从多个来源获取数据。
简而言之,在组件的状态需要共享,并且需要操作全局状态甚至更改其他组件的状态的情况下,可以考虑使用 Redux。但是需要牢记的是,Redux 只是一种架构解决方案,不是唯一的。
React-Redux 中将 React 组件分为两大类:容器组件和 UI 组件。其中 UI 组件只负责 UI 的呈现,不带有任何业务逻辑,不使用this.state
保存本地 State,所有数据都由props
提供,并且不使用任何 Redux API。所以 UI 组件与之前提到的纯函数一样,给定相同的参数,产生相同的 UI 显示。而容器组件则完全相反,只处理数据和业务逻辑,不负责 UI 呈现,并且带有内部状态,也是用 Redux API。
从原理上说,就是在 React 中创建容器组件,通过store.subscribe()
从 State 树中读取数据,并通过props提供给 UI 组件。但是 React-Redux 规定,所有的 UI 组件都由用户提供,容器组件都由 React-Redux 自动生成。所以 React-Redux 提供了connect()
方法,用于从 UI 组件中生成容器组件。
connect()
方法接受两个参数,两个参数均为函数,其中第一个参数用于将 State 映射到 Props,第二个参数用于将 Dispatch 映射到 Props。即第一个参数负责输入逻辑,第二个参数负责输出逻辑。
connect()
的具体用法可参考以下示例。
const mapStateToProps = state => {
return {
contents: state.contents.filter(c => t.onShow)
};
};
// 其中ownProps代表容器自身的props
// bindActionCreator可以将Action Creator直接放入组件供生成Action使用
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch(addContent(ownProps.newContent));
},
actions: bindActionCreators({ addContent }, dispatch)
};
};
// Contents为UI组件
const OnShowContents = connect(mapStateToProps, mapDispatchToProps)(Contents);
注意上例中mapDispatchToProps()
中返回的对象,定义了 UI 组件的参数如何发出 Action。
React-Redux 中还提供了了一个 Provider 组件,用于包裹组件树的根,使整个组件树都能拿到 State,这个原理与 React 中的 Context 一致。一般用法如下。
const store = createStore(reducers, initialState);
// Provider包裹上例中使用connect建立的容器组件
ReactDOM.render(
<Provider store={store}>
<OnShowContents />
</Provider>,
document.getElementById('app')
);
不建议直接向组件中注入全局 State,这样会导致任何一个 Action 都触发整个应用的重新渲染。最好的使用习惯是每个组件只监听它所关联的部分 State。组件可以使用以下方式注入部分关注的 State 和 dispatch
。
export default connect(state => {
related: state.related;
})(SomeElement);
如果使用 Hook 语法编写 React 组件,可以在 React 组件中使用useReducer()来返回当前的 State 和配套 Dispatch 方法。useReducer()
接受一个形如(state, action) => newState
的函数和 State 初始状态作为参数。
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>Add</button>
</div>
);
}
React-Redux v7.1 及以后的版本还提供了若干 Hook 来方便使用 Hook 方式编写的 React 组件。
useSelector(selector, equalityFn)
,用于从 Store 中提取数据。其中selector
是一个函数,用来选择要返回的 State,equalityFn
函数通过指定一个相等规则来判断 State 是否发生了变化。useDispatch()
,向组件中注入 Dispatch 方法。useStore()
,向组件中注入整个 Store。
对于前面使用 useReducer()
编写的示例,可以使用 React-Redux 提供的功能重新改写如下。
function Counter() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return (
<div>
Count: {count}
<button onClick={() => dispatch({ type: 'increment' })}>Add</button>
</div>
);
}