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()
: 获取当前state
store.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
: 简单State
useReducer
: 复杂State
useContext
: 全局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