简体   繁体   中英

Using NextJS with Redux. Should the store be re-initialised between pages with different preloaded states?

I'm trying to make Redux work with NextJS. In the official NextJS repo, you can find the following example:

https://github.com/vercel/next.js/tree/canary/examples/with-redux-thunk

Here is the main part of the Redux code (the one that initialises the store).

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
}

Here is what they say about it:

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.

Their full example is not a very good one, because none of the pages use the getInitialProps method. So you can't see the store changing when you switch pages.

// Create the store once in the client

From the comment above, I was under the impression that, no matter what, a store would only be initialised once (a single time) in the client. But that is not the case.

Every time I navigate to a page that adds a initialState to the Redux store (this is done by using the getInitialProps method), a get a new store on my client code. I've literally tested this behaviour. The store reference does change.

And that got me thinking: isn't mutating the Redux store references across multiple renders, a bad practice? I mean, React will re-render accordingly, but what about async code?

Let's say I have a Redux thunk that still pending and I navigate away from the page. Now, my Redux store will be re-initialised and what will happen if:

  • My thunk dispatch new actions? The dispatch that the thunk has is most likely linked to the previous store
  • My thunk accesses getState() ? That getState() call is also probably linked to the previous store.

Is this an anti-pattern or what?

I don't have a final answer if this is an anti-pattern or not, but there's definitely a limitation with the way NextJS suggests that the store should be initialised and re-initialised to merge server data.

I just ran the following test:

_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>
  );
}

When I navigate to pages without any pageProps.initialReduxState value, here is what I get:

在此处输入图像描述

When I navigate to pages with pageProps.initialReduxState value, here is what happens:

在此处输入图像描述

As a result from this test, I think it's safe to assume that pending code running inside a thunk will be stale and won't have any effect on the new store.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM