Redux 基础笔记

1. 什么是 Redux

Redux 是使用一个叫做“action”的事件来管理和更新应用状态的模式和工具库,它以集中式的 Store (centralized store)的方式对整个应用的使用的状态进行集中的管理,其规则保证状态只能以可以预测的方式更新。

1.1 为什么要使用 Redux

Redux 帮我们管理“全局”的状态-哪些应用程序的许多部分都需要的状态。

Redux 提供的模式和工具使我们可以更容易(?)理解应用程序中的状态何时、何地、为什么、如何更新,以及当这些更改发生的时候应用程序的逻辑将会如何表现

1.2 什么时候应该使用 Redux

Redux 在以下情况下推荐使用:

  • 在应用大量地方都存在大量的状态
  • 应用状态会随着时间的推移而频繁更新
  • 更新改状态的逻辑可能很复杂
  • 中型和大型代码的应用,很多人协同开发

并非所有应用程序都需要 Redux。花一些时间思考正在构建应用程序的类型,并决定哪些工具能最高效的解决正在处理的问题。

1.3 Redux 术语和概念

1.3.1 State 管理

让我们从一个小的 React 计数器组件开始。 它跟踪组件状态中的数字,并在单击按钮时增加数字:

function Counter() {
  // State: a counter value
  const [counter, setCounter] = useState(0)

  // Action: 当事件发生后,触发状态更新的代码
  const increment = () => {
    setCounter(prevCounter => prevCounter + 1)
  }

  // View: UI 定义
  return (
    <div>
      Value: {counter} <button onClick={increment}>Increment</button>
    </div>
  )
}

这个计数器组件包含了:

  • State:驱动应用的真实数据源头
  • view:基于当前状态 UI 声明性的描述
  • actions:根据用户输入在应用程序中发生的事件,并触发更新。

单向数据流(one-way data flow)":

  • 用 state 来描述应用程序在特定时间点的状态
  • 基于 state 来渲染出 view
  • 当发生某些事件时(例如用户点击按钮),state 会根据发生的事情进行更新,生成新的 state
  • 基于新的 state 重新渲染 view

one-way-data-flow-04fe46332c1ccb3497ecb04b94e55b97

当我们有多个组件需要共享和使用相同的 state,事情会变得很复杂,尤其是当这些组件位于不同的父组件时。

解决这个问题的一中方法是从组件中提取共享 state ,并将其放入组件树之外的一个集中位置。这样,我们的组件树就变成了一个大的 view,任何组件组件都可以访问 state 或触发 action,无论他们在树中的哪个位置!

通过定义和分离 state 管理中涉及的概念并强制维护 view 和 state 之间独立性的规则,使得代码变得更加结构化和易于维护。

这就是 Redux 背后的基本思想:应用中使用集中式的全局 state 来管理,并明确其更新状态的模式,以便让代码具有可预测行。

1.3.2 不可变性 Immutability

“Mutable” 意为 “可改变的”,而 “immutable” 意为永不可改变。

JavaScript 对象(Object)和数组(Array)默认都是 mutable 的。

const obj = { a: 1, b: 2 }
// 对外仍然还是那个对象,但它的内容已经变了
obj.b = 3

const arr = ['a', 'b']
// 同样的,数组的内容改变了
arr.push('c')
arr[1] = 'd'

内存中还是原来的对象或数组的引用,但是里面的内容变化了。

如果想要不可变的方式来更新,代码就必须先复制原来的 Object/Array ,然后更新它的复制体

JavaScript 里的展开运算符就可以实现这个操作:

const obj = {
  a: {
    // 为了安全的更新 obj.a.c,需要先复制一份
    c: 3
  },
  b: 2
}

const obj2 = {
  // obj 的备份
  ...obj,
  // 覆盖 a
  a: {
    // obj.a 的备份
    ...obj.a,
    // 覆盖 c
    c: 42
  }
}

const arr = ['a', 'b']
// 创建 arr 的备份,并把 c 拼接到最后。
const arr2 = arr.concat('c')

// 或者,可以对原来的数组创建复制体
const arr3 = arr.slice()
// 修改复制体
arr3.push('c')

Redux 期望所有状态更新都是使用不可变的方式

1.3.3 术语

Action

action 是应该具有 type 字段的普通 JavaScript 对象,可以将 action 视为描述应用程序中发生了什么事件

type 字段是一个字符串,给这个 action 一个描述性的名字,比如:“todos/todoAdded“。通常把那个类型的字符串写成:”域/事件名称“,其中第一部分是这个 action 所属的特征或类别,第二部分是发生的具体时间。

action 对象可以有其他的字段,其中包含有关发生的事情的附加信息。按照惯例我们将该信息放入 payload 字段中。例如:

const addTodoAction = {
  type: 'todos/todoAdded',
  payload: 'Buy milk'
}

Action Creator

action creator 是一个创建并返回一个 action 对象的函数。它的作用是让你不必每次都手动编写 action 对象:

const addTodo = text => {
  return {
    type: 'todos/todoAdded',
    payload: text
  }
}

Reducer

reducer 是一个函数,接收当前的 stateaction 对象,必要时决定如何更新状态,并返回新状态。函数的签名是:(state, action) => newState可以将 reducer 视为一个事件监听器,它根据接收到的 action 类型处理事件

reducer 必须符合以下规则:

  • 仅使用 state 和 action 参数计算新的 state
  • 禁止直接修改 state。必须必须通过复制现有的 state 并对复制的值进行更改的方式来做 不可变更新(immutable updates)
  • 禁止任何异步逻辑、依赖随机值或带有其他“副作用”的代码。

reducer 函数内部的逻辑通常遵循以下步骤:

  • 检查 reducer 是否关心这个 action
    • 如果是,则复制 state,并使用新值更新 state 副本,然后返回新的 state
  • 否则,返回原来的 state
const initialState = { value: 0 }

function counterReducer(state = initialState, action) {
  // 检查 reducer 是否关心这个 action
  if (action.type === 'counter/increment') {
    // 如果是,复制 `state`
    return {
      ...state,
      // 使用新值更新 state 副本
      value: state.value + 1
    }
  }
  // 返回原来的 state 不变
  return state
}

Store

当前 Redux 应用的状态存在于一个名为 store 的对象中。

store 是通过传入一个 reducer 来创建的,并且有一个名为 getState 的方法,它返回当前的 state:

import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({ reducer: counterReducer })

console.log(store.getState())
// {value: 0}

Dispatch

Redux store 有一个方法叫做 dispatch。更新 state 的唯一方法是调用 store.dispatch() 并传入一个 action 对象。store 将执行所有 reducer 函数并计算出更新后的 state,调用 getState() 可以获取最新的 state。

store.dispatch({ type: 'counter/increment' })

console.log(store.getState())
// {value: 1}

dispatch 一个 action 可以理解为“触发一个事件”。发生一些事件,我们希望 store 知道这件事。Reducer 就像事件监听器应用,当它们收到关注的 action 之后,它就会更新 state 来作为响应。

通过调用 action creator 来调用 action:

const increment = () => {
  return {
    type: 'counter/increment'
  }
}

store.dispatch(increment())

console.log(store.getState())
// {value: 2}

Selector

Selector 函数可以从 store 状态树中提取指定的片段。随着应用变得越来越大,会遇到应用程序的不同部分需要读取相同的数据,selector 可以避免重复这样的读取逻辑:

const selectCounterValue = state => state.value

const currentValue = selectCounterValue(store.getState())
console.log(currentValue)
// 2
1.3.4 Redux 数据流

“单向数据流”,它描述了更新应用程序的以下步骤序列:

  • State 描述了应用程序在特定时间点的状态
  • 基于 state 来渲染 UI
  • 当发生某些事件的时候,state 会根据当前的更新
  • 基于新的 state 重新渲染 UI

对于 Redux,可以将这些步骤分解为更详细的内容:

  • 初始启动:
    • 使用顶层 root reducer 函数创建 Redux store。
    • store 调用一次 root reducer,并将返回值作为它的初始 state
    • 当 UI 首次渲染的时候,UI 组件访问 Redux store 当前的 state,并使用该数据来决定需要渲染的内容。同时监听 store 的更新,以便它们知道 state 是否已经更改。
  • 更新环节:
    • 应用程序发生某些事件
    • dispatch 一个 action 到 Redux store ,例如 dispatch({ type: 'conter/increment' })
    • store 用之前返回的 state 和当前的 action 再次运行 reducer 函数,并将返回值保存为新的 state。
    • store 通知所有订阅过的 UI,通知他们 store 发生更新
    • 每个订阅过 store 数据的 UI 组件都会检查它们需要的 state 部分是否被更新。
    • 发现数据被更新的每个组件都强制使用新数据重新渲染,紧接着更新网页

ReduxDataFlowDiagram-49fa8c3968371d9ef6f2a1486bd40a26

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值