官网: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

  1. function Counter() {
  2. // State: a counter value
  3. const [counter, setCounter] = useState(0)
  4. // Action: code that causes an update to the state when something happens
  5. const increment = () => {
  6. setCounter(prevCounter => prevCounter + 1)
  7. }
  8. // View: the UI definition
  9. return (
  10. <div>
  11. Value: {counter} <button onClick={increment}>Increment</button>
  12. </div>
  13. )
  14. }
  • state,驱动整个 app
  • view,基于 state 声明式地描述 UI
  • actions,基于用户行为的事件触发更新 state

这是一个单向数据流的小例子

  • State 描述 app 在一个特定点的情况
  • UI 根据 state 渲染
  • 当有什么事情发生的时候(比如点击一个按钮),state 根据发生的事情更新
  • UI 根据新 state 重新渲染

image.png

5. Immutability

Immutability 意味着“不可变”。

JavaScript 的对象和数组默认是可变的,比如下面这个例子,改变了对象和数组的内容,但是它们指向的内存地址是同一个。

  1. const obj = { a: 1, b: 2 }
  2. obj.b = 3
  3. const arr = ['a', 'b']
  4. arr.push('c')
  5. arr[1] = 'd'

我们要“不可变”地更新值,可以像下面这样
Redux 希望所有 state 的更新都是“不可变”的方式

  1. const obj = {
  2. a: {
  3. c: 3
  4. },
  5. b: 2
  6. }
  7. const obj2 = {
  8. // copy obj
  9. ...obj,
  10. a: {
  11. ...obj.a,
  12. c: 42
  13. }
  14. }
  15. const arr = ['a', 'b']
  16. const arr2 = arr.concat('c')
  17. const arr3 = arr.slice()
  18. arr3.push('c')


6. Actions

一个 action 是一个有 type 字段的对象。我们可以把一个 action 认为一个事件用来描述 app 发生的事情
一个典型的 action 长得像下面这个样子

  1. const addTodoAction = {
  2. type: 'todos/todoAdded',
  3. payload: 'Buy milk'
  4. }
  • type 是一个描述性的名字,一般为 "domain/eventName"
  • paload 是参数

7. Action Creators

一个 action creater 是一个函数,用来创建并返回一个 action 对象。
我们通常不必每次手动地直接写 action 对象

  1. const addTodo = text => {
  2. return {
  3. type: 'todos/todoAdded',
  4. payload: text
  5. }
  6. }

8. Reducer

一个 reducer 是一个函数,用来接收当前的 state 和一个 action 对象,它能够决定怎么更新 state 并返回新的 state:(state, action) => newState
我们可以认为一个 reducer 是一个事件监听器,根据收到的 action 类型来处理事件。

Reducers 必须遵守下面这些特定的规则

  • 它们必须基于 stateaction 参数来计算新的 state 值
  • 它们不允许修改现有的 state,因此,它们必须通过复制现有的 state 再修改进行“不可变更新”
  • 它们不能做任何异步逻辑、计算随机值,或导致其他“side effects”

reducer 函数的逻辑一般遵循下面这些步骤

  • 检查 reducer 是否关心 action
  • 如果是的,就复制一份 state 再更新并返回它
  • 否则返回现有未变化的 state

Reducers 在内部可以使用任何类型的逻辑来决定新的 state,比如 if/elseswitch 等等

  1. const initialState = { value: 0 }
  2. function counterReducer(state = initialState, action) {
  3. // Check to see if the reducer cares about this action
  4. if (action.type === 'counter/increment') {
  5. // If so, make a copy of `state`
  6. return {
  7. ...state,
  8. // and update the copy with the new value
  9. value: state.value + 1
  10. }
  11. }
  12. // otherwise return the existing state unchanged
  13. return state
  14. }

详细解释为什么这个函数名字叫 “Reducers”?
可以参看 Array.reduce()MDN),每次处理数组中的每一项,然后最终返回单个最终的值。可以认为“把数组缩减成一个值”。

  1. const numbers = [2, 5, 8]
  2. const addNumbers = (previousResult, currentItem) => {
  3. console.log({ previousResult, currentItem })
  4. return previousResult + currentItem
  5. }
  6. const initialValue = 0
  7. const total = numbers.reduce(addNumbers, initialValue)
  8. // {previousResult: 0, currentItem: 2}
  9. // {previousResult: 2, currentItem: 5}
  10. // {previousResult: 7, currentItem: 8}
  11. console.log(total)
  12. // 15

试试创建一个 React actions 调用 reduce()

  1. const actions = [
  2. { type: 'counter/increment' },
  3. { type: 'counter/increment' },
  4. { type: 'counter/increment' }
  5. ]
  6. const initialState = { value: 0 }
  7. const finalResult = actions.reduce(counterReducer, initialState)
  8. console.log(finalResult)
  9. // {value: 3}

我们可以说 Redux reducers 把一系列的 actions 缩减成一个单个 state。
Array.reduce() 做比较

  • Array.reduce() 中,reducer 的运行是立刻的
  • Redux 中,reducer 的运行贯穿整个 app 的生命周期

9. Store

当前 Redux app state 存放在一个叫 store 的对象中
通过传入一个 reducer 来创建 store,然后它有一个 getState 方法用来返回当前的 state 值

  1. import { configureStore } from '@reduxjs/toolkit'
  2. const store = configureStore({ reducer: counterReducer })
  3. console.log(store.getState())
  4. // {value: 0}

10. Dispatch

Redux store 有一个方法叫 dispatch更新 state 的唯一方法就是调用 **store.dispatch()** 并传入一个 action 对象,store 将运行 reducer 函数并在内部保存新的 state,我们可以通过 getState() 获取新的值。

  1. store.dispatch({ type: 'counter/increment' })
  2. console.log(store.getState())
  3. // {value: 1}

我们可以认为 dispatching actions 就是在 app 中“触发一个事件”
通常调用 action creators 来 dispatch action

  1. const increment = () => {
  2. return {
  3. type: 'counter/increment'
  4. }
  5. }
  6. store.dispatch(increment())
  7. console.log(store.getState())
  8. // {value: 2}

11. Selectors

Selectors 是一个函数,用于在一个 sotore state 中知道特定的信息。
当 app 越来越大,它们可以当 app 的不同部分需要读取相同的数据,帮助避免重复的逻辑。

  1. const selectCounterValue = state => state.value
  2. const currentValue = selectCounterValue(store.getState())
  3. console.log(currentValue)
  4. // 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 是否变化了
    • 每个发现数据变化的组件会强制使用新的数据重新渲染

image.png

二、Redux DevTools

安装 Redux DevTools 插件,面板如下图所示:

  • 面板左侧是 action 触发记录
  • 面板右侧可以查看详细信息

image.png

右侧面板中:

  • “Action” 可以查看 Action 的 type 和 payload
  • “State” 可以查看 State
  • “Diff” 可以查看 Action 触发后 State 的前后变化
  • “Trace” 可以查看可以查看到 Action 触发的函数堆栈源头

「@浪里淘沙的小法师」