Redux 是什么?
redux 是一个使用叫做‘action’的事件来管理和更新应用状态的模式和工具库,以集中式Store的方式对整个应用中使用的状态进行集中管理,其规则确保状态只能以可预测的方式更新。
使用redux 的目的?
利用redux帮助管理全局状态
使用redux 的场景?
多交互、多数据源
例如: 用户的使用方式复杂、不同身份的用户有不同的使用方式、多用户之间可以协作、更新该状态的逻辑可能很复杂、View要从多个数据源获取数据;
redux库?
redux 是一个小型的独立js库,但是它通常与其他几个包一起使用
react-redux:redux可以集成在任何的ui框架中,起最常见的就是react,react-redux 可以让react组件访问state和下发action更新state,因此可以和redux集成起来。
redux toolkit: redux toolkit 是我们推荐的编写redux 逻辑的方法,它包含了对于构建redux应用程序必不可少的包和函数,它简化了大多数的redux函数,防止了常见的错误,是编写redux应用程序变得更加容易。
redux基本概念?
store: 保存数据的地方,整个应用只能有一个store
state:驱动应用的真实数据源头
view:基于当前状态的 UI 声明性描述
actions:根据用户输入在应用程序中发生的事件,并触发状态更新,是一个具有type字段的普通javascript对象,可以将action视为描述应用程序中发生了声么事件。
reducer: reducer 是一个函数,接收当前的state和一个action,必要时决定如何更新状态,并返回新的状态。可以将reducer是为一个事件监视器,根据收到的action(事件)类型处理事件。
reducer必须遵循以下规则:
仅使用state和action参数计算新的状态值;禁止直接修改state,必须通过复制现有的state并对复制的值进行更改的方式来做不可变更新;禁止任何异步逻辑、依赖随机值或导致其他‘副作用的代码’。
ruducer 函数内部的逻辑通常遵循以下步骤:
检查reducer是否关系这个action,如果是则复制action,使用新值更新state副本,饭后返回新的state,否则返回原来的state保持不变。
单向数据流(one-way data flow)?
用state来描述应用程序在特定时间点的状况,基于state来渲染出View,当发生某些事情时,state会根据发生的事情进行更新,生成新的state,基于新的state重新渲染View;
当有多个组件需要共享和使用相同的state时,尤其是当这些组件位于应用程序不同部分时,可能会很复杂,虽然有的时候可以通过提升state到父组件来解决问题,但是这并不是有效的。
解决不同组件共用同一个state的问题,有一种方法是从组件中提取共享的state,然后将其放在组件件树之外的一个集中为位置,这样,我们的组件树就变成了一个大的view,任何组件都可以访问state或出发action,无论他们在树中的哪个位置。redux 背后的基本思想就是在应用中使用集中式的全局状态来管理,并明确更新状态的模式,以便让代码具有可预测性;
redux数据流
初识启动:
使用最顶层的 root reducer 函数创建redux store,store调用一次root reducer,并将返回值作为它的初始state,当ui首次渲染时,ui组件访问redux store 的当前state,并使用该数据来决定要呈现出来的内容。同时监听store的更新,以便他们知道state是否以更改;
更新环节:
应用程序可能发生了类似用户进行点击操作这样的事件,view dispatch一个action到store,store调用之前的state和当前的action再次运行reducer函数,并将返回值保存为新的state,store通知所有订阅过的ui,通知他们store发生了更新,每个订阅过的store的ui组件都会检查他们需要的state是否发生了更新,如果发现数据发生了更新则会使用新数据进行重新渲染,紧接着更新网页。
redux toolkit
创建 redux store
Redux store 是使用 Redux Toolkit 中的 configureStore 函数创建的。configureStore 要求我们传入一个 reducer 参数。我们的应用程序可能由许多不同的特性组成,每个特性都可能有自己的 reducer 函数。当我们调用configureStore 时,我们可以传入一个对象中的所有不同的 reducer。 对象中的键名 key 将定义最终状态树中的键名 key。
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export default configureStore({
reducer: {
counter: reducerFun // 对应不同组件的reducer,state.counter是Redux state 的一个切片“slice”
}
切片
“切片”是应用中单个功能的 Redux reducer 逻辑和 action 的集合, 通常一起定义在一个文件中。该名称来自于将根 Redux 状态对象拆分为多个状态“切片”。
创建 Slice Reducer 和 Action
// features/counter/counterSlice.js
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0
},
reducers: {
increment: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1
},
decrement: state => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
}
}
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
export default counterSlice.reducer
createSlice,负责生成action类型的字符串,action creater 函数和action 对象的工作;第一个参数name 是我们为这个切片定义的一个名称,在生成的action代码中,name选项的字符串作为每个action的第一部分,第三个参数reducer中定义的函数键名作为第二部分,当我们触发了某些操作,比如调用了reducer中increment方法,那么就会dispatch 了一个 {type: "counter/increment"}的action,第二个参数initialState中,为reducer传入了初始值,方便在第一次调用时就有一个state。第三个reducer函数,对应于不同操作的action类型。
响应定义了action类型的slice reducer函数:
const newState = counterSlice.reducer(
{ value: 10 },
counterSlice.actions.increment()
)
console.log(newState)
// {value: 11}
定义reducer规则的原因:
redux的目标之一就是定义使代码变得可预测,当函数的输出的参数仅仅根据入参计算时,更容易理解代码的工作原理并对其进行测试;另一方面,如果一个函数依赖与自身之外的变量,或者行为随机,永远不知道运行时会发生什么,Redux DevTools 的一些功能取决于你的 reducer 是否正确遵循这些规则。
reducer 与不可变更新:
在redux中,永远不允许在reducer更改state的原始对象,需要先创建原始值的副本,然后改变副本
不能在 Redux 中更改 state 有几个原因:
●它会导致 bug,例如 UI 未正确更新以显示最新值
●更难理解状态更新的原因和方式
●编写测试变得更加困难
●它打破了正确使用“时间旅行调试”的能力
●它违背了 Redux 的预期精神和使用模式
手动编写不可变的更新,通过使用 JavaScript 的数组/对象扩展运算符和其他返回原始值副本的函数。这种方式手动编写不可变更新看起来很难记住和正确执行,且编写的逻辑比较繁琐,在reducer中意外改变状态是redux用户常犯的一个错误,因此使用 redux toolkit 是一个不错的选择,createSlice 内部使用了一个名为 Immer 的库。 Immer 使用一种称为 “Proxy” 的特殊 JS 工具来包装您提供的数据,当你尝试 ”mutate“ 这些数据的时候,奇迹发生了,Immer 会跟踪您尝试进行的所有更改,然后使用该更改列表返回一个安全的、不可变的更新值,就好像您手动编写了所有不可变的更新逻辑一样。
一般使用 “thunks” 来开发特定的异步逻辑
Thunks 接收 dispatch 和 getState 作为参数,使用 thunk 需要在创建时将 redux-thunk middleware(一种 Redux 插件)添加到 Redux store 中,但是,Redux Toolkit 内置并默认启用了 redux-thunk 中间件,所以可以直接使用dispatch;
使用 ****React-Redux 来做 React 组件和 Redux store 的通信
在应用程序根组件包裹 使得所有组件都能访问到 store,全局状态应该维护在 Redux store 内,局部状态应该维护在局部 React 组件内。