Redux 基础
我们都知道,像 React 框架,本地 state 是交给用户自己打理的,它们分散在组件树中,并随着数据的流动而相互影响。随着应用的复杂性提升,state 也会越来越复杂。很容易就陷入 state难管理,逻辑难追踪,应用难维护的境地。
Redux 的出现就是为了解决以上的问题。目标是让 state 可维护,可预测,可持续。Redux 原理很简单,它就是 Javascript + 设计模式(思想)。其设计模式也非常简单,可总结为:
三原则
- 单一数据源
store用来存储state state只读,只能通过action修改- 使用纯函数执行
state修改,需要编写reducers
对应三要素:
Store数据集中存储的容器。Action含有type属性的一个对象,修改数据的唯一途径,它会运送数据到Store。Reducer一个纯函数,接受当前State和Action作为参数,返回一个新的State。
Store 三方法:
store.getState(): 获取当前statestore.dispatch(action):View发出Action的唯一方法。store.subscribe(func): 订阅方法,当state改变,会触发func重新执行。
在React中应用Redux的大致流程图:
在 React 中使用 Redux 的代码示例:
import React from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
// 创建reducer纯函数计算state
const reducer = (state = 0, action) => {
switch (action.type) {
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
default:
return state;
}
};
// 使用 createStore 方法创建 store,参数为 reducer
// 多个reducer的情况下 应该有一个reducer工厂方法
const store = createStore(reducer);
const rootElement = document.getElementById("root");
const render = () => {
// 使用 getState 方法 获取state
const count = store.getState();
// 使用 dispatch 方法 发送 Action 触发state修改
const INCREMENT = () => {
store.dispatch({ type: "INCREMENT" });
};
const DECREMENT = () => {
store.dispatch({ type: "INCREMENT" });
};
ReactDOM.render(
<React.StrictMode>
<div>
<h1>{count}</h1>
<button onClick={INCREMENT}>+</button>
<button onClick={DECREMENT}>-</button>
</div>
</React.StrictMode>,
rootElement
);
};
render();
// 通过 subscribe 方法 关联 store 和 App 组件
// 当 state 改变 触发组件重新渲染
store.subscribe(render);
UseReducer Hook
useReducer hook 用于 React 函数组件中管理复杂的 state 。它把一个reducer方法,和初始state作为输入,包含当前 state,和一个 dispatch 方法的 解构数组 作为输出。
API( 对照 useState ):
// useReducer hook API
const [current, dispatch] = useReducer(reducer, initState);
// useState hook API
const [current, setFunc] = useReducer(initState);
reducer用于改变state的reducer函数,同Redux。initState初始state。current当前state。dispatch负责传递一个action给reducer函数,以此改变当前state。
和 useState 相比,useReducer 多一个reducer 函数。reducer 函数通过 dispatch 传递的 action执行不同的state修改操作。
和redux相比,用户无需关心 store 对象。另外 state 改变,组件会自动触发重新渲染。
来看一个使用 useReducer 的简单例子:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
上述示例中的 state 很简单,其实使用 useState 就可以。那么,我们什么时候该使用 useReducer 呢?
useReducer 适用场景
我们先来看看和 state 管理相关的三大 Hooks 定位:
useState: 简单StateuseReducer: 复杂StateuseContext: 全局State
适用于useReducer 的复杂 state 的场景主要有:
state逻辑较复杂且包含多个子值(大的对象,数组)。state更新依赖于之前的state。state组件树深层更新。使用useReducer可以向子组件传递dispatch而不是回调函数,这样可以优化性能。
相对于 useState, useReducer只是略复杂。所以当 state 有一定复杂度,便可以大胆使用 useReducer。我们更多的不是纠结要不要使用 useReducer,而是怎么用好 useReducer。
比如上面的代码示例中,reducer 函数可以优化一下,使其可持续发展:
...
// 用好`useReducer`的关键 - reducer 函数
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {...state, { count: state.count + 1} }; // 看这里
case 'decrement':
return {...state, { count: state.count - 1} }; // 看这里
default:
throw new Error();
}
}
...
这样改写的原因是,随着组件复杂度提升,state 对象会扩展其他属性,而不仅有 count。
参考资料
Redux 入门教程(一)
Redux 中文网
What is a Reducer in JavaScript/React/Redux?
How to useReducer in React