官网:https://redux.js.org/tutorials/essentials/part-1-overview-concepts
一、Redux 概览
1. Redux 是什么
Redux 是一种模式也是一个库,使用叫做“actions”的事件来管理和更新应用状态
(把 app、state 翻译成应用和状态在语句中读起来怪怪的,下面的篇幅中,我不翻译了,直接使用英文)
2. 什么时候使用 Redux
当有以下这些情况时,Redux 会很有用
- 在 app 中很多 state 在很多地方使用
- state 更新频繁
- 更新 state 的逻辑可能很复杂
- app 有很多代码量,很多人一起写这个 app
3. Redux 好帮手
- React-Redux
- Redux Toolkit
- Redux DevTools Extension
4. State Management
function Counter() {// State: a counter valueconst [counter, setCounter] = useState(0)// Action: code that causes an update to the state when something happensconst increment = () => {setCounter(prevCounter => prevCounter + 1)}// View: the UI definitionreturn (<div>Value: {counter} <button onClick={increment}>Increment</button></div>)}
- state,驱动整个 app
- view,基于 state 声明式地描述 UI
- actions,基于用户行为的事件触发更新 state
这是一个单向数据流的小例子
- State 描述 app 在一个特定点的情况
- UI 根据 state 渲染
- 当有什么事情发生的时候(比如点击一个按钮),state 根据发生的事情更新
- UI 根据新 state 重新渲染
5. Immutability
Immutability 意味着“不可变”。
JavaScript 的对象和数组默认是可变的,比如下面这个例子,改变了对象和数组的内容,但是它们指向的内存地址是同一个。
const obj = { a: 1, b: 2 }obj.b = 3const arr = ['a', 'b']arr.push('c')arr[1] = 'd'
我们要“不可变”地更新值,可以像下面这样
Redux 希望所有 state 的更新都是“不可变”的方式
const obj = {a: {c: 3},b: 2}const obj2 = {// copy obj...obj,a: {...obj.a,c: 42}}const arr = ['a', 'b']const arr2 = arr.concat('c')const arr3 = arr.slice()arr3.push('c')
6. Actions
一个 action 是一个有 type 字段的对象。我们可以把一个 action 认为一个事件用来描述 app 发生的事情
一个典型的 action 长得像下面这个样子
const addTodoAction = {type: 'todos/todoAdded',payload: 'Buy milk'}
- type 是一个描述性的名字,一般为
"domain/eventName" - paload 是参数
7. Action Creators
一个 action creater 是一个函数,用来创建并返回一个 action 对象。
我们通常不必每次手动地直接写 action 对象
const addTodo = text => {return {type: 'todos/todoAdded',payload: text}}
8. Reducer
一个 reducer 是一个函数,用来接收当前的 state 和一个 action 对象,它能够决定怎么更新 state 并返回新的 state:(state, action) => newState。
我们可以认为一个 reducer 是一个事件监听器,根据收到的 action 类型来处理事件。
Reducers 必须遵守下面这些特定的规则
- 它们必须基于
state和action参数来计算新的 state 值 - 它们不允许修改现有的
state,因此,它们必须通过复制现有的state再修改进行“不可变更新” - 它们不能做任何异步逻辑、计算随机值,或导致其他“side effects”
reducer 函数的逻辑一般遵循下面这些步骤
- 检查 reducer 是否关心 action
- 如果是的,就复制一份 state 再更新并返回它
- 否则返回现有未变化的 state
Reducers 在内部可以使用任何类型的逻辑来决定新的 state,比如 if/else、switch 等等
const initialState = { value: 0 }function counterReducer(state = initialState, action) {// Check to see if the reducer cares about this actionif (action.type === 'counter/increment') {// If so, make a copy of `state`return {...state,// and update the copy with the new valuevalue: state.value + 1}}// otherwise return the existing state unchangedreturn state}
详细解释为什么这个函数名字叫 “Reducers”?
可以参看 Array.reduce()(MDN),每次处理数组中的每一项,然后最终返回单个最终的值。可以认为“把数组缩减成一个值”。
const numbers = [2, 5, 8]const addNumbers = (previousResult, currentItem) => {console.log({ previousResult, currentItem })return previousResult + currentItem}const initialValue = 0const total = numbers.reduce(addNumbers, initialValue)// {previousResult: 0, currentItem: 2}// {previousResult: 2, currentItem: 5}// {previousResult: 7, currentItem: 8}console.log(total)// 15
试试创建一个 React actions 调用 reduce()
const actions = [{ type: 'counter/increment' },{ type: 'counter/increment' },{ type: 'counter/increment' }]const initialState = { value: 0 }const finalResult = actions.reduce(counterReducer, initialState)console.log(finalResult)// {value: 3}
我们可以说 Redux reducers 把一系列的 actions 缩减成一个单个 state。
与 Array.reduce() 做比较
Array.reduce()中,reducer 的运行是立刻的- Redux 中,reducer 的运行贯穿整个 app 的生命周期
9. Store
当前 Redux app state 存放在一个叫 store 的对象中
通过传入一个 reducer 来创建 store,然后它有一个 getState 方法用来返回当前的 state 值
import { configureStore } from '@reduxjs/toolkit'const store = configureStore({ reducer: counterReducer })console.log(store.getState())// {value: 0}
10. Dispatch
Redux store 有一个方法叫 dispatch,更新 state 的唯一方法就是调用 **store.dispatch()** 并传入一个 action 对象,store 将运行 reducer 函数并在内部保存新的 state,我们可以通过 getState() 获取新的值。
store.dispatch({ type: 'counter/increment' })console.log(store.getState())// {value: 1}
我们可以认为 dispatching actions 就是在 app 中“触发一个事件”
通常调用 action creators 来 dispatch action
const increment = () => {return {type: 'counter/increment'}}store.dispatch(increment())console.log(store.getState())// {value: 2}
11. Selectors
Selectors 是一个函数,用于在一个 sotore state 中知道特定的信息。
当 app 越来越大,它们可以当 app 的不同部分需要读取相同的数据,帮助避免重复的逻辑。
const selectCounterValue = state => state.valueconst currentValue = selectCounterValue(store.getState())console.log(currentValue)// 2
12. Redux Application Data Flow
前面,我们说到“单向数据流”,用来描述更新 app 的一系列步骤
- State 描述 app 在一个特定点的情况
- UI 根据 state 渲染
- 当有什么事情发生的时候(比如点击一个按钮),state 根据发生的事情更新
- UI 根据新 state 重新渲染
对于 Redux,我们可以将这些步骤分解为更多细节
- 初始设置
- 使用一个 root reducer function 创建一个 Redux store
- store 立即调用 root reducer 并保存它返回的值作为初始
state - 当 UI 首次更新,UI 组件得到 Redux store 中当前的 state,并用那数据决定渲染什么。它们并且订阅未来 store 的更新来知道 state 是否已经变化
- 更新
- app 中有东西发生了,比如点击了一个按钮
- app 代码 dispatch 一个 action 给 Redux store,比如
dispatch({type: 'counter/increment'}) - store 携带着先前的
state和当前的action再次运行 reducer 函数,然后保存返回的值作为新的state - 当 store 更新了,store 通知所有订阅过 UI 部分
- 每个需要数据的 UI 组件检查它们需要的那部分 state 是否变化了
- 每个发现数据变化的组件会强制使用新的数据重新渲染

二、Redux DevTools
安装 Redux DevTools 插件,面板如下图所示:
- 面板左侧是 action 触发记录
- 面板右侧可以查看详细信息

右侧面板中:
- “Action” 可以查看 Action 的 type 和 payload
- “State” 可以查看 State
- “Diff” 可以查看 Action 触发后 State 的前后变化
- “Trace” 可以查看可以查看到 Action 触发的函数堆栈源头
「@浪里淘沙的小法师」
