前戏:这几天在使用 Electron + React 做一个新的项目,桌面应用,在搭建框架的时候,发现数据仓库有了更好的插件,redux 的升级款 redux toolkit,在此记录下在项目中怎么使用 RTK 的。
开搞开搞 ---------》》
之前大部分童鞋应该都使用过 redux 或者自己使用 context 和 useReducer 封装过数据仓库(偷偷说下,我也自己封装了 redux ,在文章里面可以找到,哈哈 www.yuque.com/shuohaobuku…) ,即使没有使用过 redux ,也没什么问题,直接按照下面的步骤敲代码就完事了。
1. 首先是环境安装
npm install @reduxjs/toolkit react-redux -S
# 这里可以再装一个日志中间件
npm install redux-logger @types/redux-logger -D
2.使用起来
需要先创建一个 store 文件夹,然后创建一个 index.ts 文件
/*
* React Toolkit和React Redux同根同源,可以看做是React Redux的一个打包升级版。它包含了redux-thunk,immer,reselect等包,目的为了解决:
* "配置一个 Redux store 过于复杂"
* "做任何 Redux 的事情我都需要添加很多包"
* "Redux 需要太多的样板代码"
* */
/*
* Redux Toolkit的核心API主要是如下几个:
* configureStore: 包装createStore以提供简化的配置选项和良好的默认值。
* 它可以自动组合你的 slice reducer,添加你提供 的任何 Redux 中间件,redux-thunk默认包含,并启用 Redux DevTools Extension。
* createSlice: 接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions。
* createAsyncThunk: 接受一个动作类型字符串和一个返回承诺的函数,并生成一个pending/fulfilled/rejected基于该承诺分 派动作类型的 thunk
* */
import { configureStore } from '@reduxjs/toolkit';
import logger from 'redux-logger';
import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
// 引入每一个reducer
import countReducer from './modules/testSlice';
/*
* configureStore用于创建store对象
* reducer: 将slice中的reducer可以组成一个对象传入此处;
* middleware: 可以使用参数,传入其他的中间件(自行了解);
* devTools: 是否配置devTools工具,默认为true;
* */
export const store = configureStore({
reducer: {
counter: countReducer
},
// 配置中间件
// RTk已经默认使用了redux-thunk,这里不需要额外引入了
// 如果需要一些自定义的中间件,可以通过调用 getDefaultMiddleware
// 并将结果包含在返回的中间件数组中
// 案例中使用了日志的中间件,可以追踪到哪个页面在哪个时候使用了该reducer
// 并且可以显示调用前的数据状态和调用后的数据状态
middleware: (getDefaultMiddleware) => getDefaultMiddleware({}).concat(logger),
// 例如可以关闭redux-devtool
devTools: false
});
// 全局定义 dispatch 和 state 的类型,并导出
// 后面使用过程中直接从该文件中引入,而不需要冲react-redux包中引入
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// 主要解决在每次使用 useSelector 和 useDispatch 都要去重新定义 TS 类型的问题
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
再创建 modules 文件夹,里面创建要给 testSlice.ts 测试我们代码的文件
import { createSlice } from '@reduxjs/toolkit';
export interface ITestSliceData {
counter: number
}
// 定义初始化数据
const initialState:ITestSliceData = {
counter: 0 as number
};
/*
* createSlice主要包含如下几个参数:
* name:用户标记slice的名词 在之后的redux-devtool中会显示对应的名词;
* initialState:初始化值 第一次初始化时的值;
* reducers:相当于之前的reducer函数
* 对象类型,对象中可以添加很多的函数;
* 函数类似于redux原来reducer中的一个case语句;
* 函数的参数:
* 参数一: state, 当前的state状态
* 参数二: 传递的actions参数, actions有两个属性, 一个是自动生成的type, 另一个是传递的参数放在payload中;
* createSlice返回值是一个对象,包含所有的actions;
* */
// 定义一个切片
const counterSlice = createSlice({
name: 'testCount',
initialState,
// 一个包含了action的对象,每一个key都会生成一个actions(相当于原生redux的Switch Case写法)
reducers: {
// 定义一个数字递增的actions action.type为 上面的定义的name/和该对象的方法名
// 即action.type=count/increment
// 在这里一般都是使用同步的reducer
// toolkit内部调用了immer包,我们可以直接对state对象做修改,不用解构旧的state
changeCount: (state, action) => {
console.log(state, 'state');
console.log(action, 'action');
state.counter += action.payload;
}
}
});
// 导出该action
// 调用切片对象的actions属性可以获得所有在reducers里定义的actions
export const { changeCount } = counterSlice.actions;
// 导出的是 reducer, 用于在 index.js 中对 reducer 进行组合
export default counterSlice.reducer;
3.然后注册仓库到顶层组件上
首先在顶层组件中添加仓库,我这里是 App 组件
import { Provider } from 'react-redux';
import Children from './children';
import { store } from './store';
export default function App() {
return (
<Provider store={store}>
<Children />
</Provider>
);
}
4. 然后在子组件中使用
在 Children 组件中使用,这里地址引用自己匹配哈,看你在哪里创建的文件
import type { FC } from 'react';
import { useAppDispatch, useAppSelector } from '../store';
import { changeCount } from '../store/modules/testSlice';
export const Children: FC = () => {
const { counter } = useAppSelector(state => state.counter);
const dispatch = useAppDispatch();
const handleChangeCount = (val: number) => {
dispatch(changeCount(val));
};
return <div className='test'>
<h1>我是首页</h1>
<h1>-----------</h1>
<div>{counter}</div>
<h1>-----------</h1>
<button onClick={() => handleChangeCount(5)}>加5</button>
<h1>-----------</h1>
<button onClick={() => handleChangeCount(10)}>加10</button>
</div>;
};
export default Children;
以上一个简单的 demo 就创建完成了,各个配置的详情在代码里面也都有注释
后面如果踩坑了,会继续在这里更新-- !-- !--