无Hook版最原生的Redux十分复杂且麻烦,对于一个reducer需要根据传入的ActionType来执行对应逻辑。这不仅编写困难,维护也麻烦,最直观的感受就是不想再看这个代码一眼!
最新的Redux Tookit极大程度上简化逻辑处理。
React-Redux官方文档
Redux Tookit
本篇记录个人学习Redux踩过的坑及学习路线。
学习路线
理解早版本的React Redux。
对于早一些版本的项目大多采用的是原生ReactRedux,利用提供的Api进行数据读取和存放。这里不一定要完全掌握,只要理解其原理和使用方法便可。毕竟RTK实在太过便利。
学习Redux搭配RTK api
RTK提供很多便利的API可以帮助我们将每个reducer转化成独立的逻辑切片。并允许我们以对象函数形式编写每个切片的处理逻辑。
学习RTK提供的异步处理方案和中间件
最新版的RTK不需要在store里显式标注middleware,RTK已经帮你自动注册。
RTK提供creatAsnycThunk和配套的extraReducer帮助我们更好的处理异步请求数据逻辑。
学习RTK Query
一个原生基于RKT本身的请求逻辑。功能十分强大,几乎涵盖了所有方面。目前正在学习中
RTK之configureStoreApi
仓库管理员
个人理解为汇总所有reducer逻辑的仓库。
所有reducer都需要在仓库注册后才能使用。
import { configureStore } from '@reduxjs/toolkit'
export const store = configureStore({
reducer: {
//这里面存放每个reducerSlice
},
})
RTK之createSlice
仓库
对此仓库中的所有逻辑负责。
import { createSlice } from '@reduxjs/toolkit'
const initialState = { value: 0 }
const counterSlice = createSlice({
//初始化仓库名称
name: 'counter',
//初始化数据
initialState,
/*
*Action简化为对象形式。
*直接调用state对数据进行修改。
*同样可以接受一个payload参数
*/
reducers: {
increment(state) {
state.value++
},
decrement(state) {
state.value--
},
incrementByAmount(state, action) {
state.value += action.payload
},
},
})
//暴露Action给外界
export const { increment, decrement, incrementByAmount } = counterSlice.actions
//暴露仓库给仓库管理员
export default counterSlice.reducer
RTK之useSelector和useDispatch
由RTK提供的hook式函数进行数据获取和修改。
每一个Action都应在dispatch中提交,每一份数据都应该在selector中获取。
import { useSelector, useDispatch } from 'react-redux'
useSelector
完全替代connectApi。
const data = useSelector(state => state.xxx)
useSelector接受一个state参数并返回响应式数据。
useDispatch
用于提交每一个对state数据进行修改的action。
useDispatch(Action(payLoad))
RTK之createAsyncThunk
createAsyncThunk接受三个参数,一个用于定义action的String,一个函数 payloadCreator,一个option对象。并返回一个builder用于对state进行修改。
假设这里有个fetchUser的requestAPI
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId: number, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
对于接受createAsyncThunk的Api,RTK提供了三个类似promiseStatu的string内描述请求过程。
pendding fulfilled,rejected。并且会将其拼接到第一个参数后面。从而变成
pending:'users/fetchUserById/pending'fulfilled:'users/fetchUserById/fulfilled'rejected:'users/fetchUserById/rejected'对于第二个参数payloadCreator默认接受两个参数。- 第一个参数 args。若为一个参数默认直接接受,若需要传递多个参数需要师兄对象形式传递。
- 第二个参数thunkapi。由RTK提供的api帮助你处理各种情况。比较常见的如错误处理rejectWithValue。
第三个参数则是一个option对象。若有需要可以在官方文档查看搭配。这里没怎么用到过暂时不记录。
最最最重要的一步。extraReducer处理,次对象里面每一个reducer期望返回的是一个promise对象包裹的数据。
并且和正常的action一样,接受一个(state,action)参数。
下面是一个登录小demo。
获取到createAsyncThunk返回的实例,在extraReducer中根据实例中的Statu来编写对应的action。
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { getCurrentUser } from '../../api/userAPI';
import { auth } from '../../utils/auth';
export const checkAuth = createAsyncThunk('signin/checkAuth', async () => {
if (auth.isAuthenticated()) {
const token = auth.getToken();
const user = await getCurrentUser({ token });
return { token, user };
}
return { token: null, user: null };
});
export const login = createAsyncThunk('signin/login', auth.login);
export const logout = createAsyncThunk('signin/logout', auth.logout);
const initialState = {
loading: true,
error: null,
loggedIn: false,
loggedInUser: null,
token: null,
};
export const signinSlice = createSlice({
name: 'signin',
initialState,
reducers: {},
extraReducers: {
[checkAuth.pending]: startLoading,
[checkAuth.fulfilled]: (state, { payload }) => {
const { token = null, user = null } = payload;
Object.assign(state, {
loading: false,
error: null,
loggedIn: !!token,
loggedInUser: user,
token,
});
},
[checkAuth.rejected]: receiveError,
[login.pending]: startLoading,
[login.fulfilled]: (state, { payload }) => {
const { token, user } = payload;
Object.assign(state, {
loading: false,
loggedIn: true,
loggedInUser: user,
token,
});
},
[login.rejected]: receiveError,
[logout.pending]: startLoading,
[logout.fulfilled]: (state) =>
Object.assign(state, {
...initialState,
loading: false,
}),
[logout.rejected]: receiveError,
},
});
function startLoading(state) {
Object.assign(state, {
loading: true,
error: null,
});
}
function receiveError(state, action) {
Object.assign(state, {
loading: false,
error: action.error,
});
}
export const selectSignin = (state) => state.signin;
export const signinReducer = signinSlice.reducer;
学到这里略微有点迷茫,哎呦,不知道继续看RTK query还是使用其开源请求包。