需要两个插件吖,React DevTools Extension for Chrome 和 Redux DevTools Extension for Chrome
(还有 for FireFox版本捏)
action 纯纯对象
reducer 纯纯函数
土木er 纯纯牛马
Redux解决什么问题吖?
redux提供了一个集中式存储数据的 store 仓库,这个仓库中的数据可以对整个应用中使用的状态数据进行集中管理,它负责保存状态,当数据更新,订阅仓库的组件也能收到更新后的数据。
redux使用一种叫做 action 的时间来管理和更新状态,这种action能确保store中数据的更新方式是可预测的。(也就是很死板、很机械化、模式化的更新方式)
啥时候使用redux?
- 如果app中的 很多 state 需要在 很多组件中使用
- 如果state会随着时间推移频繁更新
- 如果更新state的逻辑很复杂
- 如果你的应用规模在中型以上(5-10人以上协作)
redux需要的工具链
需要配合 react-redux、redux-toolkit、redux devTools 食用,风味更佳。
react-redux是react和redux的 连通器,他可以让react应用品尝到redux中的state,还能让react发射action来更新state。(useSelector能获取数据,useDispatch提供发射器)
其中redux-toolkit是新版本的 redux,它已经包含了redux的core内容,是redux开发者们呕心沥血创造出的船新一袋redux工具库,是官方推荐的现代化最佳实践(2023年2月)。请放心食用。
redux术语介绍
state管理
我们一般用useState 来存储简单的 状态,比如下面的东东。
注意到没有哇?一个调用setCounter改变状态的函数 被定义为 action。
所以一个action 和 事件吖、用户操作和改变数据紧紧结合在一起。
function Counter() {
// State: counter 值
const [counter, setCounter] = useState(0)
// Action: 当事件发生后,触发状态更新的代码
const increment = () => {
setCounter(prevCounter => prevCounter + 1)
}
// View: 视图定义
return (
<div>
Value: {counter} <button onClick={increment}>Increment</button>
</div>
)
}
这里包含了一个单向数据流的概念, state是唯一的数据源头,view根据state进行渲染展示,action是用户触发的事件,可以触发状态state更新。
state更新后,view重新渲染。
当有多个组件需要共享使用同一个state,就会变得复杂而繁琐,默认情况下我们会提升state到父组件,但是这不是总有效。
解决方法很多,我们一般把 被多个组件共享的state提取并提升到组件树之外的一个集中的位置,这样,组件树成为一个更大的view, 组件树里的任何组件都能访问state和触发action。
这种方法:
- 抽离的state,状态好管理
- 让view和state相互独立,结构化,不混乱
这就是redux的基本思想,集中式的状态存储,全局可用。另外,redux的更新模式可确定,所以变化有可预测性。
不可变性
Redux 期望所有状态更新都是使用不可变的方式。
如果你不知道什么是不可变性,emmmm,就好比一个object {a:1,b:2},你想要“不可变”地更新他,就只能复制这个obj,再更新复制后的obj。。。。
术语
啊,终于到这里了,redux术语让小白头疼,但是稍微一了解,就发现竟然如此简单。
action:前面说他和事件有关,那个太广义了,实际上是事件触发一个事件处理函数,当你要用redux的方式更新数据,你就要触发一个action。
那么什么是一个action? 它就是一个对象,记得前面说更新数据的模式很固定? 就是因为action的设计,这个action一点也不灵活,以至于过去我们在使用redux的时候使用(虽然是自定义的但其实是官方和社区鼓励的最佳实践)actionCreator来创造action(当然这个后面再说也可以,现在最新版的rtk提供的createSlice 就直接提供了actionCreator),说到底,过去的actionCreator就是一个创造对象的工厂。
所以,action就是一个 形式固定的对象,他的基本形式就是
const addTodoAction = {
// 域 所属于的特征或类别/事件名称 ,也就是发生的具体事件
type: 'todos/todoAdded',
//附加信息 ,比如你要更新的字符串,你要更新的数字。。。
payload: 'Buy milk'
}
Action Creator:emmm,就是个快速返回action对象的工厂,emmmm,当action多了之后,我们都会想办法避免编写这个东东。。。,这里介绍这个,我想更多地是为了向后兼容。。
Reducer: 这么说吧,action触发更新,store接收到action,就调用一个对应的reducer来更新数据。
所以,你看reducer和state、action紧密结合,所以reducer能接收 state和action作为参数,当reducer被调用,他能通过action决定怎么更新状态,并返回新的状态。
(state,action)=>newState。
前面说action是 事件(他和click其实层级一样,就是向某个东西报告:“xxx发生了,还接收到了一些参数”)
那么reducer就是事件监听器里边的处理函数吖, body.addEventLister("action",reducer); 对吧?
另外 看这个东东
const aReducerFn =(accumulator,currentVal)=>accumulator +currentVal;
let initialVal= 0;
const sum =[1,2,3,4].reduce(aReducer,initialVal)
文档上面说,就是因为 redux的reducer类似reduce中的 aReducer ,所以叫做reducer.
注意!使用reducer的时候
- 必须只使用state和action来计算数值
- 必须复制当前state,更改复制后的state,也就是必须不可变地变更state
- 禁止异步逻辑,禁止依赖随机值,或导致其他副作用的代码
简单一句话, reducer必须是个纯函数(笑)
前面说了,reducer接收action,他会根据action来判断是否更新state,“所谓是否关心这个action”,
1.检查 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 :
是一个对象,state存在这里,通过传入一个reducer来创建store(emmm,rtk是这么做的)
dispatch:
之前说了,action发给 store,store里的reducer接收action和state来更新数据,那么谁发送action?
store有个dispatch方法,而且store.dispatch方法是唯一更新state的方法吖。
store执行所有的reducer并且计算出更新后的state。
而调用store.getState 可以获取最新的state。
dispatch一个action 就可以形象地看成 触发一个事件,我们希望store知道这个事情,reducer是事件处理函数,接收到action后就更新state。
selector:
可以从store中选取特定的数据片段 ,select 片段嘛。。
Redux数据流
前面已经提到了数据流,现在基于Redux更详细说说
初始启动:
- 使用最顶层的root reducer创建 store
- store调用一次root reducer,将返回值保存为其初始state
- view访问store的state,并渲染。同时监听store的更新,以便随时更新视图
更新环节: - 应用中发生了变换,比如用户点击
- dispatch一个action到store
- reducer被store调用,并传入state和action,reducer返回最新的状态值,store保存最新状态值
- store通知订阅过的视图
- UI根据更新来检查是否更新视图
- 有必要地重新渲染