在网上搜索了下Redux,发现都是两三年前的相关文章,当时的redux确实非常难用,但现在最新版已经支持了react hooks,变得非常的易用和理解。
拉开最新redux序幕前,先来看下面2个问题:
Redux是什么?
它是整个应用的全局状态(数据)容器,定义的state将贯穿所有的组件,意味着它里面的state都可以被整个应用的任何组件访问和修改。并且当该state被修改,将会同步更新到所有组件中。
你什么时候用Redux?可以问问自己以下几个问题
- 在多个不同(跨)组件/页面中使用和访问这些state吗?
- state在未来随时会发生变化吗?
- 更新这些全局state和依赖该state的UI会很困难吗?(假如你用localStorage作全局数据管理,当数据变更时,依赖该数据的组件是否还得手动操作才能使用更新后的数据?)
一个工具当你真正需要时,会让你生活轻松,并不需要时,拥有它其实是一种负担。
建议在回答了上面3个问题后,再决定是否安装在应用中,当将来应用增大,有需要时,随时都可以再安装使用。当然有时由于接口设计不合理,也会导致看似需要redux,这时你得跟后台的家伙聊聊,勇敢些,去改造他!
深入理解和使用
先花点时间看下面这幅图,它清楚地描绘了redux的state更新过程。注意:state不是直接变更的,而是通过dispatch 一个action。
图中整个过程有3步分别是:
- 初始
state=10,点击deposit(取款)10$触发事件后, - 调用
dispatch,发送deposit(取款10)的action, - 然后经过
reducer进行匹配,执行相匹配的action的逻辑,state被-10,此时state=0,然后你看到UI被更新为0$。
由上图可知,redux核心使用为以下2步:
- 定义
state - 定义
reducer,里面的每个action,描述了如何改变state
下面以官方例子来说明,使用以下命令创建redux项目:
npx create-react-app my-app --template redux
创建后你会看到一个计数器,你可以对着例子分别找到以下几个api来学习。
最新版主要提供了createSlice、createAsyncThunk、useSelector、useDispatch等几个api,非常的简单,来了解下它们吧。
createSlice和createAsyncThunk
1. createSlice
使用createSlice直接创建state和reducers,现在核心完成了,reducers字段里定义了改变state的action,比如increment和decrement。
2. createAsyncThunk
"thunk"在编程中,表示一些做会延迟工作的代码
createAsyncThunk创建的是异步的action,比如setTimeout/setIntervel/promise这种异步/产生延迟的工作。最常见就是网络请求了,看下面的fetchCount的异步代码,然后在extraReducers的builder的addcase方法,你可以把它理解为switch语句,添加你要的action,也支持异步action的状态,当incrementAsync被dispatch时,就会分别捕获并执行incrementAsync.pending和incrementAsync.fulfilled里的代码。
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
const initialState = {
value: 0,
status: 'idle',
};
function fetchCount(amount = 1) {
return new Promise((resolve) =>
setTimeout(() => resolve({ data: amount }), 500)
);
}
export const incrementAsync = createAsyncThunk(
'counter/fetchCount',
async (amount) => {
const response = await fetchCount(amount);
return response.data;
}
);
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(incrementAsync.pending, (state) => {
state.status = 'loading';
})
.addCase(incrementAsync.fulfilled, (state, action) => {
state.status = 'idle';
state.value += action.payload;
});
},
});
使用useSelector访问/使用state
useSelector接收一个带state的函数,编写返回你想使用的state的即可。
import { useSelector } from 'react-redux';
export function Counter() {
const count = useSelector((state) => state.counter.value);
return (
<div>
{count}
</div>
);
}
使用useDispatch变更state
useDispatch接收的是你在createSlice中定义的reducers里的action,比如把increment传给useDispatch即可给state加1。
import {useSelector, useDispatch } from 'react-redux';
import {
decrement,
increment,
} from './counterSlice';
import styles from './Counter.module.css';
export function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<div>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
<span>{count}</span>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
+
</button>
</div>
</div>
);
}
注意
state只能通过dispatch来更改connect相关api已不被官方推荐
为什么可预料是redux的特性之一呢?
因为变更state的时候,需要dispatch一个action,action描述了如何改变state的逻辑啊,你只要理解了action的代码行为逻辑,不就知道state是如何被改变的了,所以就能预料了。
总结:
createSlice创建state和reducer(actions)useSelector获取stateuseDispatch变更state。
有木有突然发现状态管理就像数123一样简单?