RTK工具包
Redux Toolkit
是官方推荐用于编写Redux
逻辑的工具包- 由于
Redux
的逻辑编写过于繁琐,并且代码通拆分在多个文件中,Redux Toolkit
旨在成为编写Redux
逻辑的标准方式,从而解决代码繁琐的问题 - 安装
Redux Toolkit
shell
npm i @reduxjs/toolkit
Redux Toolkit
的核心API
主要如下:
- configureStore: 包装
createStore
以提供简化的配置选项和良好的默认值,可以自动组合拆分的reducer
,添加提供的任何Redux
中间件(默认包含redux-thunk
),并启用Redux DevTools
- createSlice: 接受
reducer
函数的对象、切片名称和初始状态值,自动生成切片reducer
,并带有相应的actions
- createAsyncThunk: 接受一个
actionType
字符串和一个返回Promise
的函数,生成一个基于该Promise
分派actionType
的thunk
基本使用
- 原来的拆分模式是每个模块都有属于各自的
reducer
,actionCreators
,使用RTK
工具包对其重构 RTK
工具包官网: redux-toolkit.js.org/
通过
createSlice
创建切片,createSlice
主要包含以下参数和返回值
- name: 标记切片的名词,会在
redux-devtool
中显示 - initialState: 第一次初始化的值
- reducers: 由函数组成的对象,相当于
reducer
函数,内部的函数类似每个action
操作 - 返回值: 一个对象,包含所有的
actions
和reducer
javascript
// store/counter.js
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: {
num: 888
},
reducers: {
addNum(state, action) {
state.num += action.payload; // 这里做了优化,不需要返回新的state
},
subNum(state, action) {
state.num -= action.payload;
}
}
})
export const { addNum, subNum } = counterSlice.actions
export default counterSlice.reducer
使用
configureStore
创建store
对象,常见参数如下:
- reducer: 将
slice
中的reducer
组成一个对象传入 - middleware: 可以使用参数,传入其他的中间件,默认集成
redux-thunk
和redux-devtool
- devTools: 是否配置
devTools
工具,默认为true
javascript
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './modules/counter';
const store = configureStore({
reducer: {
counter: counterReducer
},
devTools: true
})
export default store
- 组件中使用还是通过
react-redux
的Provider
共享和connect
映射
异步操作(createAsyncThunk)
Redux Toolkit
默认已经集成了Thunk
相关的功能:createAsyncThunk
javascript
import { createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
export const fetchBannersAction = createAsyncThunk('fetch/banners', async () => {
const { data: { data } } = await axios.get(url);
return data
})
使用
createAsyncThunk
创建出来的action
被dispatch
时,存在三种状态:
- pending:
action
被发出,但还没有最终的结果 - fulfilled: 成功获取到最终的结果
- rejected: 执行过程中出现错误或者抛出异常
javascript
const homeSlice = createSlice({
name: 'home',
initialState: {
banners: []
},
reducers: {},
// 针对异步的action
// extraReducers使用对象的写法在RTK2.0中准备弃用
extraReducers: (builder) => {
builder.addCase(fetchBannersAction.pending, (state, action) => {
console.log(action.type); // fetch/banners/pending
})
.addCase(fetchBannersAction.fulfilled, (state, action) => {
state.banners = action.payload.banner.list;
})
.addCase(fetchBannersAction.rejected, (state, action) => {
console.log(action.type);
})
}
})
- 在不用
extraReducers
的情况下,使用普通的reducers
也可改变state
中的数据
createAsyncThunk
中的回调函数还有两个额外的参数:
- extraInfo :额外信息,在调用
createAsyncThunk
返回的Action
函数时传入的数据 - store: 完整的
store
javascript
export const fetchBannersAction = createAsyncThunk('fetch/banners', async (extraInfo, store) => {
const { data: { data } } = await axios.get('http://123.207.32.32:8000/home/multidata');
store.dispatch(changeBanners(data.banner.list)) // 这里可以直接分发action
})
const homeSlice = createSlice({
name: 'home',
initialState: {
banners: []
},
reducers: {
changeBanners(state, action) {
state.banners = action.payload
}
},
})
export const { changeBanners } = homeSlice.actions;
export default homeSlice.reducer;
- 像上面的做法也可以分发
action
(本人比较喜欢),但还是使用官方推荐的extraReducers
数据不可变(Immutable)
- 在
React
开发中,无论是类组件中的state
,还是redux
中管理的state
,甚至在整个JavaScript
编码过程中,数据的不可变性都是非常重要的,所以React
经常会进行浅拷贝来完成某些操作
浅拷贝事实上也是存在问题:
- 对过大的对象进行浅拷贝会造成性能的浪费
- 浅拷贝后的对象,在作深层改变时,依然会对之前的对象产生影响
javascript
const friends = [
{ name: 'Jimmy' }
];
const newFriend = [ ...friends ];
newFriend[0].name = 'James' // 这里改变newFriend,friends里的对象也会被改变
- 事实上
Redux Toolkit
底层使用了 immerjs 库来保证数据的不可变性 Immutable
对象的特点: 只要修改了对象,就会返回一个新对象,旧对象不会发生改变
为了节约内存使用新的算法:Persistent Data Structure(持久化数据结构)
- 用一种数据结构来保存数据
- 当数据被修改时会返回一个对象,但新对象会尽可能利用之前的数据结构,而不会对内存造成浪费