Redux 数据仓库的数据持久化和 Next.js 中使用流程 | JustPure 起始页的构建历程

grtsinry43
3/7/2025(更新于 3/7/2025
144 views
预计阅读时长 7 分钟

AI Summary

Powered By DeepSeek-R1
|

最近写了一个浏览器的起始页,是我无数忙碌任务中拼命抽时间赶出来的,很简陋但也是这段时间的一点心血,在无数忙碌生活中留下一个小小的自己的栖息罢了。

JustPure 起始页——摒弃繁杂,唯有纯粹

目前网站位于 cf 全球网络 (国内减速网络),之后我想办法优化回国带宽体验,也会写一个浏览器插件使之直接运行于本地,减少网络开销和卡顿。

界面和设计

额,这次就是我大面积用了 shadcn/ui,跟着组件库走还是可以的,有大佬反馈太花哨...我是计划提供切换一个模糊的关闭功能 (其实之前写了但是实在写不完了,那个开关被我注释掉了)

image

Redux 的数据持久化

主要是想讲一下这个,首先是在 Next.js 中可以直接参考官方文档:

Redux Toolkit Setup with Next.js

我的计划是维护一个数据仓库,并且会实时同步到 localstorage 做数据持久化

思路是这样的:

首先 Redux 提供了一个 preloadedState 部分,这允许我们先预先加载一部分数据,我们可以利用这个将 localstorage 的内容加载进来,而对于保存,Redux,提供了一个事件订阅 subscribe,我们可以在这里异步进行存储和更新。

思路很明确了,那我们就可以直接开始写了:

加载状态

typescript
1const loadState = () => {
2    if (typeof window === 'undefined') {
3        return undefined;
4    }
5    try {
6        const serializedState = localStorage.getItem('appState');
7        if (serializedState === null) {
8            return undefined;
9        }
10        return JSON.parse(serializedState);

保存/覆盖状态

typescript
1const saveState = (state: RootState) => {
2    if (typeof window === 'undefined') {
3        return;
4    }
5    try {
6        const serializedState = JSON.stringify(state);
7        localStorage.setItem('appState', serializedState);
8    } catch (err) {
9        console.error(err);
10    }

于是我们可以在构建 store 中使用这两个函数

typescript
1export const makeStore = () => {
2    const store = configureStore({
3        reducer: {
4            //...(你的 reducers)
5        },
6        preloadedState: loadState(),
7    });
8
9    if (typeof window !== 'undefined') {
10        store.subscribe(() => {

如果你使用的是 js,那么恭喜你已经没有问题了,但是如果是 ts,...这种写法类型推断就无法正常运行了

原因在哪里呢?

其实很简单我们看函数签名就一目了然了,由于 JSON.parse 返回了 any,整个函数返回了 undefined|any,导致类型与 store 不匹配,那么我们就要着手解决这个问题。

我们先集中定义类型:

typescript
1// 1. 先集中定义所有 reducers
2const reducers = {
3    //...(你的 reducers)
4};

然后我们要手动推导 RootStore 的类型:

typescript
1// 2. 基于 reducers 推导 RootState 类型
2export type RootState = {
3    [K in keyof typeof reducers]: ReturnType<typeof reducers[K]>
4};

拿到了类型就可以在加载数据的时候进行类型断言啦:

typescript
1// 3. 这里解析后断言为 RootState 类型(需确保存储结构与当前 reducers 匹配)
2        return JSON.parse(serializedState) as RootState;

此时我们组合之前的函数:

typescript
1export const makeStore = () => {
2    const store = configureStore({
3        reducer: reducers, // 使用集中定义的 reducers
4        preloadedState: loadState(), // 现在类型明确为 RootState | undefined
5    });
6
7    if (typeof window !== 'undefined') {
8        store.subscribe(() => {
9            saveState(store.getState());
10        });

此时的类型推断就正常啦:

image

完整代码直接参考我的仓库:https://github.com/grtsinry43/pure-start/blob/main/lib/store.ts

谈谈想法

这个是我最近真的是拼命挤出时间写的,连这篇文章也很仓促,无论评价是精美还是花哨我都慢慢去改进,提供自定义和多个用户体验,对我来说就当是熟悉这个组件库了,还是练手的成分更多


此项目正处于早期开发阶段,欢迎提出建议和贡献代码

https://github.com/grtsinry43/pure-start

COPYRIGHT
作者grtsinry43
版权年份© 2025
许可协议

Redux 数据仓库的数据持久化和 Next.js 中使用流程 | JustPure 起始页的构建历程》采用知识共享署名 4.0 国际许可协议

转载请注明出处并遵循 CC BY 许可协议条款

相关推荐

零成本手把手带你建一个学习笔记网站(AriaDocs 项目的使用简记)

偶然逛 Vercel 遇到的,本来我是有计划搞一个类似的文档的,并且计划随便搭个 Wikijs,或者...

grtsinry43
3/2/2025
164
2
1

登山节微信网页实现思路简述 | 实现定位和数据处理

去年 10 月份左右,学校组织活动需要,要求 7 天内实现一个登山节打卡用微信网页,包含定位打卡,排...

grtsinry43
3/23/2025
83
1
2

使用Spring Security快速实现Oauth2客户端配置

在Spring的强大生态中,我们很多时候只需要简单的配置就可以完成想要的需求。在Grtblog中,我...

grtsinry43
12/15/2024
375
0
0

动手部署 Grtblog 前后端,即刻拥有自己的博客网站

开学了比较忙,没想到一下子拖了这么久,有好多小伙伴评论和 b 站私信评论催我,赶快我抽时间弄了下部署...

grtsinry43
2/19/2025
1883
4
6
使用 pf4j-spring 实现插件注入和 api 接口动态注册 | 插件系统构建(上)

使用 pf4j-spring 实现插件注入和 api 接口动态注册 | 插件系统构建(上)

哪个男孩不想拥有一个自己的插件系统?(x)话说回来,这个我已经计划好久了,不过一直在学其他的东西,刚...

grtsinry43
1/26/2025
511
0
5
COMMENT 7303624178769465344

发表评论

来这里畅所欲言吧!
支持 Markdown 语法 0 / 3000

网站运行时间

0
0
0
0

在风雨飘摇之中

感谢陪伴与支持

愿我们不负热爱,继续前行