将 NextJS 与 Redux 结合使用。商店是否应该在具有不同预加载状态的页面之间重新初始化?
Using NextJS with Redux. Should the store be re-initialised between pages with different preloaded states?
我正在尝试让 Redux 与 NextJS 一起工作。在官方 NextJS 仓库中,您可以找到以下示例:
https://github.com/vercel/next.js/tree/canary/examples/with-redux-thunk
这是 Redux 代码的主要部分(初始化商店的代码)。
import { useMemo } from 'react'
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunkMiddleware from 'redux-thunk'
import reducers from './reducers'
let store
function initStore(initialState) {
return createStore(
reducers,
initialState,
composeWithDevTools(applyMiddleware(thunkMiddleware))
)
}
export const initializeStore = (preloadedState) => {
let _store = store ?? initStore(preloadedState)
// After navigating to a page with an initial Redux state, merge that state
// with the current state in the store, and create a new store
if (preloadedState && store) {
_store = initStore({
...store.getState(),
...preloadedState,
})
// Reset the current store
store = undefined
}
// For SSG and SSR always create a new store
if (typeof window === 'undefined') return _store
// Create the store once in the client
if (!store) store = _store
return _store
}
export function useStore(initialState) {
const store = useMemo(() => initializeStore(initialState), [initialState])
return store
}
他们是这样说的:
Every initial server-side request will utilize a new store. However, every Router or Link action will persist the same store as a user navigates through the pages. To demonstrate this example, we can navigate back and forth to /show-redux-state using the provided Links. However, if we navigate directly to /show-redux-state (or refresh the page), this will cause a server-side render, which will then utilize a new store.
他们的完整示例不是很好,因为 none 的页面使用了 getInitialProps
方法。所以当你切换页面时你看不到 store
变化。
// Create the store once in the client
从上面的评论来看,我的印象是,无论如何,一个商店只会在客户端初始化一次(一次)。但事实并非如此。
每次我导航到将 initialState
添加到 Redux 存储的页面(这是通过使用 getInitialProps
方法完成的)时,都会在我的客户端代码中获取一个新存储。我已经从字面上测试了这种行为。 store
引用确实发生了变化。
这让我开始思考:跨多个渲染改变 Redux store
引用不是一种糟糕的做法吗?我的意思是,React 会相应地重新渲染,但是异步代码呢?
假设我有一个仍在等待处理的 Redux thunk,我离开了该页面。现在,我的 Redux store 将被重新初始化,如果出现以下情况会发生什么:
- 我的 thunk 发出新动作? thunk 的调度很可能链接到以前的商店
- 我的 thunk 访问
getState()
?该 getState()
电话也可能与之前的商店有关。
由于商店重新初始化,是否有一些注意事项?
尽管 Next.js 文档中没有提到这一点,但我认为 NextJS 建议初始化和重新初始化存储以合并服务器预加载数据的方式肯定存在局限性。
我只是运行下面的测试:
_app.tsx
import { useEffect } from "react";
import { Provider } from "react-redux";
import { useStore } from "./useStore";
export default function App({ Component, pageProps }) {
console.log("Rendering App...");
const store = useStore(pageProps.initialReduxState);
const dispatch = store.dispatch;
const getState = store.getState;
useEffect(() => {
console.log("store has changed");
}, [store]);
useEffect(() => {
console.log("dispatch has changed");
}, [dispatch]);
useEffect(() => {
console.log("getState has changed");
}, [getState]);
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
}
当我导航到没有任何 pageProps.initialReduxState
值的页面时,我得到的是:
当我导航到具有 pageProps.initialReduxState
值的页面时,会发生以下情况:
作为这个测试的结果,我认为可以安全地假设 thunk 中的待处理代码 运行 将过时并且不会对新商店产生任何影响。
Redux 指出 Redux 在服务器上最常见的用例是预加载页面的初始渲染。从那时起,正如他们所说:客户接管渲染任务。
https://redux.js.org/usage/server-rendering
我正在尝试让 Redux 与 NextJS 一起工作。在官方 NextJS 仓库中,您可以找到以下示例:
https://github.com/vercel/next.js/tree/canary/examples/with-redux-thunk
这是 Redux 代码的主要部分(初始化商店的代码)。
import { useMemo } from 'react'
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunkMiddleware from 'redux-thunk'
import reducers from './reducers'
let store
function initStore(initialState) {
return createStore(
reducers,
initialState,
composeWithDevTools(applyMiddleware(thunkMiddleware))
)
}
export const initializeStore = (preloadedState) => {
let _store = store ?? initStore(preloadedState)
// After navigating to a page with an initial Redux state, merge that state
// with the current state in the store, and create a new store
if (preloadedState && store) {
_store = initStore({
...store.getState(),
...preloadedState,
})
// Reset the current store
store = undefined
}
// For SSG and SSR always create a new store
if (typeof window === 'undefined') return _store
// Create the store once in the client
if (!store) store = _store
return _store
}
export function useStore(initialState) {
const store = useMemo(() => initializeStore(initialState), [initialState])
return store
}
他们是这样说的:
Every initial server-side request will utilize a new store. However, every Router or Link action will persist the same store as a user navigates through the pages. To demonstrate this example, we can navigate back and forth to /show-redux-state using the provided Links. However, if we navigate directly to /show-redux-state (or refresh the page), this will cause a server-side render, which will then utilize a new store.
他们的完整示例不是很好,因为 none 的页面使用了 getInitialProps
方法。所以当你切换页面时你看不到 store
变化。
// Create the store once in the client
从上面的评论来看,我的印象是,无论如何,一个商店只会在客户端初始化一次(一次)。但事实并非如此。
每次我导航到将 initialState
添加到 Redux 存储的页面(这是通过使用 getInitialProps
方法完成的)时,都会在我的客户端代码中获取一个新存储。我已经从字面上测试了这种行为。 store
引用确实发生了变化。
这让我开始思考:跨多个渲染改变 Redux store
引用不是一种糟糕的做法吗?我的意思是,React 会相应地重新渲染,但是异步代码呢?
假设我有一个仍在等待处理的 Redux thunk,我离开了该页面。现在,我的 Redux store 将被重新初始化,如果出现以下情况会发生什么:
- 我的 thunk 发出新动作? thunk 的调度很可能链接到以前的商店
- 我的 thunk 访问
getState()
?该getState()
电话也可能与之前的商店有关。
由于商店重新初始化,是否有一些注意事项?
尽管 Next.js 文档中没有提到这一点,但我认为 NextJS 建议初始化和重新初始化存储以合并服务器预加载数据的方式肯定存在局限性。
我只是运行下面的测试:
_app.tsx
import { useEffect } from "react";
import { Provider } from "react-redux";
import { useStore } from "./useStore";
export default function App({ Component, pageProps }) {
console.log("Rendering App...");
const store = useStore(pageProps.initialReduxState);
const dispatch = store.dispatch;
const getState = store.getState;
useEffect(() => {
console.log("store has changed");
}, [store]);
useEffect(() => {
console.log("dispatch has changed");
}, [dispatch]);
useEffect(() => {
console.log("getState has changed");
}, [getState]);
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
}
当我导航到没有任何 pageProps.initialReduxState
值的页面时,我得到的是:
当我导航到具有 pageProps.initialReduxState
值的页面时,会发生以下情况:
作为这个测试的结果,我认为可以安全地假设 thunk 中的待处理代码 运行 将过时并且不会对新商店产生任何影响。
Redux 指出 Redux 在服务器上最常见的用例是预加载页面的初始渲染。从那时起,正如他们所说:客户接管渲染任务。
https://redux.js.org/usage/server-rendering