官网地址:redux.js.org
以下是我使用redux-toolkit库的一个目录结构:
.
├── README.md
├── package.json
├── tsconfig.json
├── next.config.mjs
└── app
├── layout.tsx
└── page.tsx
└── storeProvider.tsx
└── store
├── features
│ ├── counter.ts
│ ├── counterSlice.ts
│ ├── counterApi.ts
└── index.ts
└── hooks.ts
└── createAppSlice.ts
附上所用版本:
"next": "14.1.4",
"react": "^18",
"@reduxjs/toolkit": "^2.2.3",
"react-redux": "^9.1.0"
安装
在项目中使用redux,一般要装 redux、react-redux、redux-thunk 三种插件。
@reduxjs/toolkit 相当于安装了redux的同时也内置了thunk中间件。
yarn add @reduxjs/toolkit
yarn add react-redux
使用
redux主要分为三部分,store 、action、 reducer 。
创建store
// store/index
// 在入口文件导出创建store的函数
import { combineSlices, configureStore } from "@reduxjs/toolkit";
import { counterSlice } from "./features/counter/counterSlice";
// combineSlices相当于redux的combineReducers,把reducer组合起来
const rootReducer = combineSlices(counterSlice);
export const makeStore = () => {
// 通过configureStore创建store,相当于redux的createStore
return configureStore({
// 参数传reducer就可以
reducer: rootReducer,
// middleware默认就是thunk。使用thunk的话下面可以不写,可以自定义
middleware: (getDefaultMiddleware) => {
return getDefaultMiddleware();
},
});
};
创建slice
slice将reducer和action封装到了一起,所有的相关操作都独立在一个文件中完成。
// store/features/counter/counterSlice
import { createAppSlice } from "@/store/createAppSlice";
import type { AppThunk } from "@/store/index";
import type { PayloadAction } from "@reduxjs/toolkit";
import { fetchCount } from "./counterAPI";
export interface CounterSliceState {
value: number;
status: "idle" | "loading" | "failed";
}
const initialState: CounterSliceState = {
value: 0,
status: "idle",
};
// createAppSlice是自定义文件中导出的buildCreateSlice函数,它可以使slice中的action具有thunk的功能
// 你仍然可以在slice之外导出一个副作用action
export const counterSlice = createAppSlice({
// 命名空间,可以自动的把每一个action进行独立,解决了action的type出现同名的文件
// 在使用的时候默认会把使用name/actionName
name: "counter",
// state数据的初始值
initialState,
reducers: (create) => ({
increment: create.reducer((state) => {
state.value += 1;
}),
decrement: create.reducer((state) => {
state.value -= 1;
}),
incrementByAmount: create.reducer(
(state, action: PayloadAction<number>) => {
state.value += action.payload;
}
),
// 这是通过create.asyncThunk创建一个副作用action
incrementAsync: create.asyncThunk(
async (amount: number) => {
const response = await fetchCount(amount);
// 这里的返回值会进入到fulfilled的action.payload中
return response.data;
},
{
pending: (state) => { state.status = "loading" },
fulfilled: (state, action) => {
state.status = "idle";
state.value += action.payload;
},
rejected: (state) => { state.status = "failed" },
}
),
}),
selectors: {
selectCount: (counter) => counter.value,
selectStatus: (counter) => counter.status,
}
});
// 导出action
// 尤其是slice中的副作用action,用于组件中使用
export const { decrement, increment, incrementByAmount, incrementAsync } =
counterSlice.actions;
export const { selectCount, selectStatus } = counterSlice.selectors;
// 自定义的副作用action
export const incrementByAsync =
(amount: number): AppThunk =>
(dispatch, getState) => {
setTimeout(() => {
dispatch(incrementByAmount(amount));
}, 1000);
};
在组件中使用
// app/layout
import { StoreProvider } from "./storeProvier";
import "./globals.css";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
// 在最外层包裹一层provider
<StoreProvider>
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
</StoreProvider>
);
}
// app/page
"use client";
import Link from "next/link";
import { useEffect, useRef } from "react";
import { useAppDispatch, useAppSelector } from "@/store/hooks";
import {
incrementAsync,
incrementByAsync,
} from "@/store/features/counter/counterSlice";
export default function Home() {
// 使用dispatch
const dispatch = useAppDispatch();
// 使用store
const counter = useAppSelector((state) => state.counter) || {};
const onChangeCounter = () => {
// 纯action => counter就是之前定义slice时的name,再加上reducer中action的名字
dispatch({ type: "counter/increment" });
// 副作用action => incrementAsync是slice中定义的函数action
dispatch(incrementAsync(5))
// 副作用action => 可以写但不推荐,业务逻辑多了会很复杂,redux的目的就是让你专注于偏平的action
dispatch(
((value) => (dispatch) => {
setTimeout(() => {
dispatch({
type: "counter/incrementByAmount",
payload: value,
});
}, 1000);
})(2)
);
// 副作用action => 同上
setTimeout(() => {
dispatch({ type: "counter/increment" });
}, 1000)
};
return (
<main className="flex min-h-screen flex-col">
<code className="font-mono font-bold">welcome to next!</code>
<div>当前counter的值为: {counter.value}</div>
<Link href="#" onClick={onChangeCounter}>
点击改变counter的值
</Link>
<Link href="/about">关于</Link>
</main>
);
}
我之前用过redux + thunk和react + saga,光说配置其实感觉大同小异。
不知道大家觉得redux-toolkit是否好用呢。
附上项目地址:快! 点我