在 Next 14 的 appRouter 模式中接入 React-Redux
说明
Next.js
版本升级到 14 后,相比 13 版本是一个改动很大的大版本升级,很多概念或者使用方式 13 版本都有较大的区别,因此这里记录一些学习 14 版本的 Next.js
的心得体会或者问题。因为我这边构建项目选择的是 Next.js
新的路由模式 App Router
,因此该文档是基于 App Router
路由模式的。
安装依赖
根据 react-redux
官方文档 的说明,使用如下面命令安装依赖:
pnpm add @reduxjs/toolkit react-redux
创建 store 模块
我们以创建一个 counterSlice
举例:
在项目根目录(或者 app 目录,或者其他目录),创建 store
目录,以及 react-redux
的主文件 store.ts
,然后创建 /storemodules/counterSlice.ts
,并写入如下代码:
//counterSlice.jsx
"use client"; //this is a client side component
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "../store";
const initialState = {
value: 0,
};
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;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
export const selectCounter = (state: RootState) => state.counter.value;
**注意:**这里需要注意,在 next
中,redux
需要作为客户端渲染的模块,因此 store
模块的文件头部都需要加上使用客户端渲染的注解 "use client";
。
然后在 store.ts
里面写入如下代码:
//store.jsx
"use client";
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import counterReducer from "./modules/counterSlice";
import { Provider } from "react-redux";
const rootReducer = combineReducers({
counter: counterReducer,
//add all your reducers here
});
export const store = configureStore({
reducer: rootReducer,
});
export function ReduxProvider({ children }) {
return <Provider store={store}>{children}</Provider>;
}
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
将 ReduxProvider
作为组件抛出去。
使用定义好的 store 模块
注册 Provider
我们可以在全局 layout
里面注册 Provider
, 这样能保证我们的所有的客户端组件都能使用 Redux
:
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import ThemeRegistry from "@/app/components/themeRegistry/ThemeRegistry";
import HeaderBar from "@/app/components/layout/HeaderBar";
import { ReduxProvider } from "@/app/store/store";
import { useEffect } from "react";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App11",
description: "Generated by create next app",
};
function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className={inter.className}>
<ReduxProvider>
<ThemeRegistry>
<HeaderBar />
{children}
</ThemeRegistry>
</ReduxProvider>
</body>
</html>
);
}
// export default wrapper.withRedux(RootLayout);
export default RootLayout;
组件中使用 redux
之前已经说了,redux
在 next.js
中只能是作为客户端渲染模块使用,所以我们不能再任何的 page.tsx
路由页面组件中使用(除非这个路由页面有客户端渲染组件注解use client;
,然而这种情况可能并不多见。),因此对于需要使用 redux
的地方,我们需要这块儿逻辑封装成客户端渲染的组件,比如:
"use client";
import {
decrement,
increment,
selectCounter,
} from "@/app/store/modules/counterSlice";
import { AppDispatch } from "@/app/store/store";
import { Box, Button, Typography } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
export default function CounterControl() {
const counter = useSelector(selectCounter);
const dispatch = useDispatch<AppDispatch>();
const handleChangeCounter = (type: "ADD" | "MINUS") => {
dispatch(type === "ADD" ? increment() : decrement());
};
return (
<Box>
<Typography variant="h1">{counter}</Typography>
<Box>
<Button variant="outlined" onClick={() => handleChangeCounter("ADD")}>
ADD
</Button>
<Button
variant="outlined"
onClick={() => handleChangeCounter("MINUS")}
sx={{ ml: 2 }}
>
MINUS
</Button>
</Box>
</Box>
);
}
这样子我们就可以在任意组件(包括路由组件 page.tsx
)里面使用封装的这个 CounterControl
组件了:
import { Metadata } from "next";
import { Button } from "@mui/material";
import NavigateButton from "@/app/components/tools/NavigateButton";
import CounterControl from "../components/counter/CounterControl";
export const metadata: Metadata = {
title: "Users page",
description: "Generated by create next app",
};
export default function UsersPage() {
return (
<div>
<CounterControl />
<Button sx={{ mx: 1 }} variant="contained">
Hellow Mui
</Button>
<NavigateButton destination="/" variant="contained" sx={{ mx: 2 }}>
back
</NavigateButton>
<h2>This is the User Index page</h2>
</div>
);
}
以上就完成了在 Next.js 14
的 App Router
路由模式中接入 react-redux
的全过程。