Typescript 如何在页面刷新时在NextJS中正确地添加Redux状态?

Typescript 如何在页面刷新时在NextJS中正确地添加Redux状态?,typescript,react-redux,next.js,redux-toolkit,Typescript,React Redux,Next.js,Redux Toolkit,我遇到的问题是在应用程序重新加载/页面刷新时从本地存储水合用户数据 在我的项目中,我将NextJS用于前端,对于支持库,我将使用redux toolkit用于整个应用程序的redux管理,并使用next redux wrapper用于包装页面的状态管理 用户可以登录,在这种情况下,我将isLoggedIn布尔值存储在本地存储中并处于redux状态。根据isLoggedIn布尔值,我更改Navbar组件样式(Navbar直接包含在\u app.tsx中) 当用户刷新任何页面时,isLoggedIn

我遇到的问题是在应用程序重新加载/页面刷新时从本地存储水合用户数据

在我的项目中,我将NextJS用于前端,对于支持库,我将使用
redux toolkit
用于整个应用程序的redux管理,并使用
next redux wrapper
用于包装页面的状态管理

用户可以登录,在这种情况下,我将
isLoggedIn
布尔值存储在本地存储中并处于redux状态。根据
isLoggedIn
布尔值,我更改
Navbar
组件样式(
Navbar
直接包含在
\u app.tsx
中)

当用户刷新任何页面时,
isLoggedIn
boolean不会加载到状态,而是存在于本地存储器中

在过去,我一直在使用
redux persist
,但我选择不使用它,因为
PersistGate
会阻止UI呈现,直到从存储中获取持久化数据,这与SSR的思想相冲突

目前我使用
\u App.ts
中的
App.getInitialProps
方法解决了
isLoggedIn
加载问题,这会导致为每个加载的页面调用
下一个redux persist
,但这带来了另一个问题:所有页面现在都是服务器端呈现的,没有NextJS的静态页面优化

有没有办法不丢失NextJS的静态页面优化,不使用
redux persist
库,并且在刷新任何页面时仍然能够在客户端存储中添加水合物

当前代码结构(为了简单起见,省略了一些代码):

文件:_app.tsx
从“存储”导入{wrapper};
常量MyApp=({Component,pageProps}:AppProps)=>{
返回(
);
};
MyApp.getInitialProps=async(appContext)=>{
const-appProps=await-App.getInitialProps(appContext);
返回{…appProps};
};
导出默认包装器。withRedux(MyApp);
文件:store.ts
导入{
联合传感器,
配置存储,
EnhancedStore,
getDefaultMiddleware
}来自“@reduxjs/toolkit”;
从“下一个redux包装器”导入{createWrapper,MakeStore};
从“lib/slices/userSlice”导入userReducer;
const rootReducer=combinereducer({
用户:userReducer
});
const setupStore=(上下文):EnhancedStore=>{
常量中间件=[…getDefaultMiddleware(),thunkMiddleware];
if(process.env.NODE_env===‘development’){
推送(记录器);
}
返回配置存储({
还原剂:根还原剂,
中间件,
//预加载状态,
devTools:process.env.NODE_env==='development'
});
};
const makeStore:makeStore=(上下文)=>setupStore(上下文);
export const wrapper=createWrapper(makeStore{
调试:process.env.NODE_env==='development'
});
文件:userSlice.ts
从'@reduxjs/toolkit'导入{createSlice};
从“下一个redux包装器”导入{HYDRATE};
常量初始状态={
伊斯洛格丁:错
}
export const userSlice=createSlice({
名称:“用户”,
初始状态,
减速器:{
登录:(状态)=>{
state.isLoggedIn=true;
setItem('loggedInData',{isLoggedIn:true});
}
},
外部减速器:(生成器)=>{
建设者
.addCase(水合物)(状态、作用:任何)=>{
如果(窗口类型!==“未定义”){
const storedLoggedInData=localStorage.getItem('loggedInData');
if(storedLoggedInData!=null&&storedLoggedInData){
const parsedJson=JSON.parse(storedLoggedInData);
state.isLoggedIn=parsedJson.isLoggedIn??false;
}否则{
state.isLoggedIn=false
}
}
});
}
});
export const isLoggedInSelector=(state:RootState)=>state.user.isLoggedIn;
导出默认的userSlice.reducer;
文件:Navbar.tsx
从'react redux'导入{useSelector};
从'lib/slices/userSlice'导入{isLoggedInSelector};
导出默认函数Navbar(){
const isLoggedIn=使用选择器(isLoggedInSelector);
返回(
...
)
}

文档中提到,如果在_app.js中使用getInitialProps,您将失去静态优化。我不知道你为什么在服务器端使用redux,我个人会建议你只在客户端使用它,你不再需要使用next redux包装器,因为它使用getInitialProps


带有

的示例今天也有同样的问题。问题是我们需要将客户端存储与页面呈现分离,并将其移动到安装组件的
useffect
中。基本思想是,首先完全呈现页面,然后使用客户机存储信息更新页面

直接结合客户机本地存储可能/将干扰水合作用

下面是我使用的一个代码示例

export default const MenuBar = () => {
  const isLoggedIn = useSelector((state) => state.isLoggedIn);
  useEffect(() => {
    // loads from clients local storage
    const auth = loadAuthenticationToken(); 
    if (auth !== null) {
      // updates the store with local storage data
      dispatch(actions.jwtTokenUpdate(auth)); 
    }
  }, [dispatch]);
  
  if (isLoggedIn) {
    return <p>you are logged in</p>;
  } else {
    return <p>please log in</p>;
  }
}
导出默认常量菜单栏=()=>{
const isLoggedIn=useSelector((state)=>state.isLoggedIn);
useffect(()=>{
//从客户端本地存储加载
const auth=loadAuthenticationToken();
if(auth!==null){
//使用本地存储数据更新存储
调度(actions.jwtTokenUpdate(auth));
}
},[发送];
如果(isLoggedIn){
返回您已登录的;
}否则{
返回请登录;
}
}
作为参考,NextJS上的github问题:


还有一篇需要使用窗口进行渲染的博文:

我已经阅读了关于
App.getInitialProps
的文档,但我不确定在这种情况下如何避免它。对于某些页面,我使用
getServerSideProps
获取SSR的数据。我使用redux thunk进行API调用,它们将响应直接路由到redux。我不想跳过redux作为一个单一的真相来源。这可能是个不好的理由吗