kirill-konshin / next-redux-wrapper

Redux wrapper for Next.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Duplicate initialState from client response

phatify opened this issue · comments

commented

Describe the bug

I am using next redux wrapper in App.getInitialProps but in client response I get duplicated initialState are: initialState outside pageProps and inside pageProps. You can see the image below:

Screenshot 2022-11-26 154315

To Reproduce

Steps to reproduce the behavior:

Happens everytime when navigate to another page.

Expected behavior

initialState object in pageProps is not necessary and need to remove it.

Screenshots

Screenshot 2022-11-26 154315

Desktop (please complete the following information):

  • OS: Windows 10 Pro
  • Browser: Chrome
  • Version: Lastest

Additional context

No response

It's actually not a complete duplicate. Check out my explanation here: https://github.com/kirill-konshin/next-redux-wrapper/pull/499/files#r1014500941

This package wraps around the server side data fetching calls: getInitialProps in _app (GIP), getStaticProps (GSP) and getServerSideProps (GSSP) (and getInitialProps for pages, but you should be using GSSP instead). These wrappers take the store state and put its state in a key called initialState, alongside the actual props that the GIP/GSP/GSSP return. GIP and GSSP/GSP set initialState keys in different places; check the link above for the details.

You're suggesting that if GIP runs and there's a GSSP, that we should remove the initialState key set by GIP. However, because the code that sets the initialState key in GIP is run during during the getInitialProps phase, there's no way to know if a getServerSideProps is going to run later. These functions aren't aware of each other.

And if there is no GSSP, then we need the initialState from GIP.

So I don't think it can be removed. Thoughts @kirill-konshin?

commented

@voinik Excuse me, but your avatar looks so cute. haha.

I think initialState is using as a global state and it shouldn't containing in pageProps. If it is contained in pageProps, it is no different if we provide prop initialState like below:

const getServerSideProps = () => {
    return {
       initialState: ...
    }
}

So what do we need global state for?

Remember that initialState is always contained in pageProps even though we have return pageProps or not.
Example:

App.getInitialProps = wrapper.getInitialPageProps((store) => (context) => {
     store.dispatch(mySlice.actions.addSticky(...));
    // only dispatch actions and don't return anything here but initialState still contain in pageProps. What is happened?
})

I need a convincing explanation !!!

commented

@voinik Excuse me, but your avatar looks so cute. haha.

Thanks! 😁

I think I understand your confusion. Let's go through it step by step:

  1. We want to use redux and build a store when a new page needs to be loaded
  2. We want to do dispatches server side in Next's GIP/GSP/GSSP that mutate the store for the new page
  3. We want the store that was built on the server with those dispatches to be sent to the client somehow
  4. We don't know if the developer that uses this wrapper has a GIP, and/or a GSP, and/or a GSSP for their pages beforehand (important!)
  5. So if we're in GIP, or GSP, or GSSP, we need to pass the store data we've built inside GIP/GSP/GSSP somehow
  6. The only way we can do that is by passing the store into the data that GIP/GSP/GSSP returns
  7. BUT Next.js has some rules regarding what you can return in GIP/GSP/GSSP!
  8. The data that GIP returns can be any data, but it must at least contain a pageProps key. So in GIP it doesn't matter where we put the store data. During this step we name the key with the store data: initialState and do not put it in pageProps
  9. The data that GSP/GSSP returns must only contain a prop key. If you add another key, Next.js throws an error. So we have no choice but to put the store data inside the prop key. So during this step we name the key with the store data: initialState too, which becomes props.initialState
  10. Finally, as I explain in https://github.com/kirill-konshin/next-redux-wrapper/pull/499/files#r1014500941, Next.js merges the data returned in step 7 and 8 together. It spreads the GSP/GSSP props key into the GIP pageProps key (read the post I linked for an example), so the props.initialState from step 9 becomes pageProps.initialState
  11. Now there are multiple scenarios that can happen:
    a. If there is a GIP and NO GSP/GSSP (which we don't know beforehand! See step 4), then our complete store exists in initialState (not inside pageProps) (see step 8)
    b. If there is a GSP/GSSP and NO GIP (which we don't know beforehand! See step 4), then the store we need exists in pageProps.initialState (see step 9)
    c. If there is a GSP/GSSP AND a GIP (which we don't know beforehand!!!!!! See step 4), then the store resulting from the GSP/GSSP calls will have the newest data, as GSP/GSSP runs after GIP. Therefore, we need the store found in pageProps.initialState (see step 9). But because GIP has run, we will still have the first version of the store inside initialState (see step 8). So we have 2 versions, one old one (initialState from GIP) and one new one (pageProps.initialState from GSP/GSSP). BUT We didn't know we wouldn't need the GIP store when we were running GIP! GIP doesn't know if there is a GSP/GSSP! So we can't do anything but have both.

TLDR; We dispatch data in the server inside GIP/GSP/GSSP. We need that store data on the client. The only way to get data out of GIP/GSP/GSSP is by putting that data into the app props, because that's how Next.js works. So to get the store on the client, we need to pass the store to the app props. If your app has both GIP and GSP/GSSP, your app props will have 2 initialStates (initialState and pageProps.initialState), because we don't know beforehand if there will be a GIP and/or GSP/GSSP or not.

As for this:

I think initialState is using as a global state and it shouldn't containing in pageProps. If it is contained in pageProps, it is no different if we provide prop initialState like below:

const getServerSideProps = () => {
    return {
       initialState: ...
    }
}

So what do we need global state for?

You're right, it's no different. But that's the point of this package: it does some stuff for you so that you don't have to do it yourself. It's basically (roughly) doing this:

const state = store.getState();
const props = runGetXProps(); // Runs the GIP/GSP/GSSP in your application for the current route
return {
    initialProps: props, // this is not entirely how it's done; we are ignoring the rules in step 8 and 9 here, but this is a rough example
    initialState: state,
};

If you remove this, every user of this package would have to write this themselves for every single page with a getServerSideProps/getStaticProps, and/or getInitialProps. We don't want to do that.

Hopefully this answers your questions 🙂