next-redux-wrapper icon indicating copy to clipboard operation
next-redux-wrapper copied to clipboard

next-redux-wrapper usage slows down route change

Open pablohpsilva opened this issue 2 years ago • 14 comments

Describe the bug

Whenever I use next-redux-wrapper, changing routes starts to get really slow.

export const getServerSideProps: GetServerSideProps<I18nProps<MyLocale>> = getStore((store) => async (ctx) => {
// ...
  store.dispatch(setFeatureFlags(flags));
// ...
  store.dispatch(updateDevice(getSelectorsByUserAgent(ctx.req.headers["user-agent"])));
  return ...;
});

With the code above, whenever I change routes, it takes around 10s to change on dev and 5s on prod. If I remove it, it becomes instant, like milliseconds.

To Reproduce

The code is proprietary, I can't fork or clone it.

Steps to reproduce the behavior:

Just use this library with some dependencies like usedapp, or theming, or any UI lib. Check how long it takes to change routes. Remove it, and you'll see the difference.

Expected behavior

Using redux shouldn't impact the app routing load time.

Screenshots

If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 6]

Additional context

pablohpsilva avatar May 28 '22 21:05 pablohpsilva

hi , i had this problem too, you can use 8.0.0-rc.1 instead of 7.0.5 there is some changes

for create store you must use configureStore

const makeStore = () =>
    configureStore({
        reducer: appReducer,
        devTools: true,
    });

export const wrapper = createWrapper(makeStore, {debug: false});

and in _app.js:

const App = ({Component, ...rest}) => {
    const {store, props} = wrapper.useWrappedStore(rest);
    return (
        <Provider store={store}>
                <Component {...props.pageProps} />
        </Provider>
    )
}

roshaninet avatar May 29 '22 07:05 roshaninet

That RC is pretty good. Thank you @roshaninet

pablohpsilva avatar May 29 '22 14:05 pablohpsilva

When can we expect the RC to be ready? Do you need help with testing/docs

michielmetcake avatar May 31 '22 13:05 michielmetcake

RC looks pretty OK, I have not received any bug reports for quite a while, so I might promote it. Stay tuned, I will release a new major version shortly.

P.S. The only thing that might require extra effort is new app dir with slightly alternative fetching mechanism, which includes React Server Components. I am still figuring out how to add support for it.

kirill-konshin avatar May 31 '22 14:05 kirill-konshin

Updating to 8.0.0-rc.1 did help me get rid of this issue, but now HYDRATE is only called on initial page load, but not during regular page navigation. any ideas?

frederikvanhevel avatar Jun 02 '22 15:06 frederikvanhevel

I noticed this useEffect (https://github.com/kirill-konshin/next-redux-wrapper/blob/master/packages/wrapper/src/index.tsx#L178) is never being called, event though initialStateFromGSPorGSSR is clearly a different value. So hydrate is never being called in my case. I'm clueless why this is happening.

frederikvanhevel avatar Jun 05 '22 08:06 frederikvanhevel

I've encountered this issue as well, and in my case it's because, on client-side navigation with next/link and next/router, identical HYDRATE actions are repeatedly dispatched - 700ish times per navigation for me in dev mode. I think it puts the app into a spinlock until some async task finishes - something, somewhere (possibly not in next-redux-wrapper) is not waiting when it should. I have not found the root cause.

Using React 18.2, Next 12.1.6, next-redux-wrapper 7.0.5. Other possibly relevant libraries are RTK Query with @reduxjs/toolkit 1.8.2 and connected-next-router 4.1.1 - calling them out because they both do interesting things on route change.

Here's what I found out and a workaround that seems to work for my case, in case they're useful to you @kirill-konshin or anyone else having this problem. I am using App-level getInitialProps only.

It's coming from withRedux.shouldComponentUpdate: nextProps.initialState and this.props.initialState are different repeatedly, so this.hydrate is called repeatedly.

I think what's supposed to happen is: HYDRATE is dispatched, the store is updated, this.props has the updated store, that makes this.props === nextProps, and we move on. But something happens in that process and React tries to re-render before the store is updated, so WrappedApp's props aren't updated yet, so it dispatches HYDRATE again. Repeat until... this is where I got stuck.

I overrode WrappedApp.prototype.shouldComponentUpdate with an identical body, with the this.hydrate call in a timeout. 0 timeout was the same, but waiting 10-50ms reduced the number of dispatches and waiting 100-150ms eliminated the duplicate dispatches. (This is why I wonder if RTK Query is involved - 50-100ms is on the network request timeframe.)

In my use case (but not necessarily others!), changing route always changes the Next page. So if we've already dispatched a HYDRATE for this.props.Component, we know the store is updated. So my workaround is to remember the last page component we hydrated for, and only hydrate if it's different from this page component:

const WrappedApp = wrapper.withRedux(App);

let lastComponent;
WrappedApp.prototype.shouldComponentUpdate = function(nextProps, nextState, nextContext) {
  if (nextProps?.pageProps?.initialState !== this.props?.pageProps?.initialState
    || nextProps?.initialState !== this.props?.initialState) {
      if (lastComponent !== this.props.Component)  {
        this.hydrate(nextProps, nextContext);
        lastComponent = this.props.Component;
      }
  }
  return true;
};

My app doesn't use page-level getInitialProps or getServerSideProps, it doesn't look like this would break those but I haven't tested that case. It might break if you render the same page on different routes though!

RJFelix avatar Jun 25 '22 08:06 RJFelix

any update on releasing version 8?

madmed88 avatar Aug 11 '22 12:08 madmed88

I can release it, it's been in beta for quite some time.

kirill-konshin avatar Aug 12 '22 19:08 kirill-konshin

Hi, I am getting warning when upgrading to 8.0.0-rc.1 from 7.0.5, my implementation is same as the readme guide /!\ You are using legacy implementaion. Please update your code: use createWrapper() and wrapper.useWrappedStore().

https://github.com/kirill-konshin/next-redux-wrapper/tree/8.0.0-rc.1#app

kc980602 avatar Aug 28 '22 10:08 kc980602

Here is how you can use the new API https://github.com/kirill-konshin/next-redux-wrapper/blob/8.0.0-rc.1/packages/demo-redux-toolkit/pages/_app.tsx

madmed88 avatar Aug 28 '22 13:08 madmed88

I can release it, it's been in beta for quite some time.

Hi! Any update on this? We would love an official 8.0.0 release. Thanks for all your work on this!

lenzi-erickson avatar Aug 30 '22 20:08 lenzi-erickson

TypeError: redux_store__WEBPACK_IMPORTED_MODULE_5_.wrapper.useWrappedStore is not a function


//MyApp.js
function MyApp({ Component, ...rest }) {
  const {store, props} = wrapper.useWrappedStore(rest);
  return (
    <>
     <Provider store={store}>
     <Navbar />
      <Component  {...props.pageProps}  />
     </Provider>
    </>
  );
}
export default MyApp

//reducer.js
const combinedReducer=combineReducer({
 login: loginReducer,
    participants: addParticipantReducer,
    trainings: addTrainingsReducer,
})
const reducer = (state, action) => {
  if (action.type === HYDRATE) {
    const nextState = {
      ...state, // use previous state
      ...action.payload, // apply delta from hydration
    };
    return nextState;
  } else {
    return combinedReducer(state, action);
  }
};

export const makeStore = () =>
  configureStore({
    reducer,
  });

export const wrapper = createWrapper(makeStore, { debug: true });

I have upgraded from v7.0.5 to 8.0.0-rc.1 . v7.0.5 made routes really slow but now 8.0.0-rc.1 is throwing this error. What can I do this get this working @kirill-konshin @madmed88 ?

carefree-ladka avatar Sep 01 '22 18:09 carefree-ladka

I use 8.0.0-rc.1 and all the navigation works perfectly except locale change:

<Link
    href={router?.asPath ?? "/"}
    locale={otherLocale}
    passHref
>

As soon as I add locale, the page switch takes a long time and redux creates +-700 states/commits

ioulian avatar Sep 06 '22 13:09 ioulian

TypeError: redux_store__WEBPACK_IMPORTED_MODULE_5_.wrapper.useWrappedStore is not a function

//MyApp.js
function MyApp({ Component, ...rest }) {
  const {store, props} = wrapper.useWrappedStore(rest);
  return (
    <>
     <Provider store={store}>
     <Navbar />
      <Component  {...props.pageProps}  />
     </Provider>
    </>
  );
}
export default MyApp

//reducer.js
const combinedReducer=combineReducer({
 login: loginReducer,
    participants: addParticipantReducer,
    trainings: addTrainingsReducer,
})
const reducer = (state, action) => {
  if (action.type === HYDRATE) {
    const nextState = {
      ...state, // use previous state
      ...action.payload, // apply delta from hydration
    };
    return nextState;
  } else {
    return combinedReducer(state, action);
  }
};

export const makeStore = () =>
  configureStore({
    reducer,
  });

export const wrapper = createWrapper(makeStore, { debug: true });

I have upgraded from v7.0.5 to 8.0.0-rc.1 . v7.0.5 made routes really slow but now 8.0.0-rc.1 is throwing this error. What can I do this get this working @kirill-konshin @madmed88 ?

upgrade next-redux-wrapper to version 8

marcosjampietri avatar Oct 20 '22 17:10 marcosjampietri

Not sure if it related but I'm finding state updates are generally very sluggish when using next-redux-wrapper this is when I use

const { store, props } = wrapper.useWrappedStore(rest);

dottodot avatar Feb 25 '23 12:02 dottodot

Not sure if it related but I'm finding state updates are generally very sluggish when using next-redux-wrapper this is when I use

const { store, props } = wrapper.useWrappedStore(rest);

Can you provide measurements? I have not noticed any performance degradation. It could be due to your reducers. Wrapper has minimal overhead, just a few very small loops over simple objects.

kirill-konshin avatar Feb 28 '23 20:02 kirill-konshin