GithubHelp home page GithubHelp logo

bjoluc / next-redux-cookie-wrapper Goto Github PK

View Code? Open in Web Editor NEW
113.0 5.0 3.0 4.16 MB

Sync a subset of your Redux state with cookies in Next.js :cookie: :sparkles:

License: MIT License

TypeScript 98.36% Shell 0.25% JavaScript 1.39%
nextjs redux cookies ssr next-redux-wrapper

next-redux-cookie-wrapper's Introduction

next-redux-cookie-wrapper

npm (tag) GitHub Workflow Status codecov XO code style semantic-release

A next-redux-wrapper extension to sync a subset of a client's Redux state with cookies such that it survives page reloads and is available to the server during SSR 🍪 ✨

Motivation

When it comes to Redux state persistence, Redux Persist is a popular choice. With Next.js, however, the persisted state is (without further ado) not available during SSR. Hence, the first render on the client side may largely differ from the server-rendered markup. A solution to this is a storage method that is available to both the server and the client by default: Cookies.

This library started as a drop-in replacement for next-redux-wrapper that built upon Redux Persist and a storage adapter for cookies. However, in response to getStaticProps() and getServerSideProps() being introduced in Next.js, it has been rewritten and the tooling has been simplified significantly. What remains is a single Redux middleware and a tiny wrapper around the makeStore() function.

How does it work?

On the server, a wrapper around makeStore() passes the Next.js context to the middleware via an action. The middleware then reads the cookies and dispatches an initial HYDRATE action with the client's state. On server-side state changes, set-cookie headers are set to update the client's cookies.

Similarly, the client updates cookies whenever a relevant portion of the state changes. Moreover, the HYDRATE action is intercepted on the client and the configured state subtrees are (by default) parsed from the cookies instead of the retrieved JSON data. This way, incoming state updates from getStaticProps() do not overwrite the synced state subtrees as getStaticProps() does not update the cookies. You can opt out of this behavior on a per-state-subtree basis and instead always receive the server's state in the HYDRATE reducer if you wish to handle state portions from getStaticProps() on your own.

Some words about compression: By default, the serialized cookie state is compressed using lz-string to keep the cookie size small. You can disable compression globally or per state subtree by setting the compress option to false.

Setup

TL;DR

For a quick working example, check out the demo project in this repository. It uses Redux Toolkit but that should not discourage you.

  • Clone the repository
  • Make sure you have have NPM v7 or later installed (npm i -g "npm@>=7"; required for the workspaces feature)
  • Run npm install in the root directory
  • cd demo && npm start
  • Inspect the setup in store.ts

If you do not have next-redux-wrapper set up, follow their installation instructions. Afterwards, install next-redux-cookie-wrapper:

npm install --save next-redux-cookie-wrapper

and configure your store to use nextReduxCookieMiddleware by passing it to createStore() and wrapping your makeStore() function with wrapMakeStore():

+ import {nextReduxCookieMiddleware, wrapMakeStore} from "next-redux-cookie-wrapper";

...

- const makeStore = () => createStore(reducer);
+ const makeStore = wrapMakeStore(() =>
+   createStore(
+     reducer,
+     applyMiddleware(
+       nextReduxCookieMiddleware({
+         subtrees: ["my.subtree"],
+       })
+     )
+   )
+ );

That's it! The state of my.subtree should now be synced with a cookie called my.subtree and available on the server during SSR. If not, you can set the debug flag of next-redux-wrapper to true to get some insights on the state:

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

Usage with Redux Toolkit

When using Redux Toolkit, it is important that nextReduxCookieMiddleware is used before any of the default middlewares:

const makeStore = wrapMakeStore(() =>
  configureStore({
    reducer: {...},
    middleware: (getDefaultMiddleware) =>
      getDefaultMiddleware().prepend(
        nextReduxCookieMiddleware({
          subtrees: ["my.subtree"],
        })
      ),
  })
);

The reason for this is that Redux Toolkit by default adds a serializability middleware that would complain about the SERVE_COOKIES action which wrapMakeStore() uses to pass the Next.js context to nextReduxCookieMiddleware. When nextReduxCookieMiddleware is invoked before the serializability middleware, it catches the SERVE_COOKIES action before it reaches that middleware. Alternatively, you can also configure the serializability middleware to ignore the SERVE_COOKIES action, should you prefer that.

Configuration

For the configuration options of nextReduxCookieMiddleware, please refer to the API documentation.

next-redux-cookie-wrapper's People

Contributors

bjoluc avatar dependabot[bot] avatar heinthanth avatar semantic-release-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

next-redux-cookie-wrapper's Issues

how to add redux-logger in diff env

import logger from "redux-logger";

export const initStore = configureStore({
  reducer: combinedReducers,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().prepend(
      nextReduxCookieMiddleware({
      subtrees: ["auth.accessToken", "auth.refreshToken", "auth.isLogin", "auth.me"],
    })
// todo: how set logger when env = development 
    ).concat(logger)
})

this is not work!

.concat(process.env.NODE_ENV === `development` ? logger: ()=>{})

CookieConfig not being used on client

I noticed that my cookieConfig settings weren't taking effect on the client, whether it was changing the persist key or the cookie expiration.

The root cause seems to be that the cookieConfigs are only being used on the server.

storage: new CookieStorage(cookies, {
...sharedCookieConfig,
setCookieOptions: {
...sharedCookieConfig.setCookieOptions,
httpOnly: false, // Allow modifications on the client side
},
}),

Whereas on the client, no cookieConfig is being used:

storage: new CookieStorage(ClientCookies),

storage: new CookieStorage(cookies),

LZ string: network request to pieroxy/lz-string/tar.gz/b2e0b270a9f3cf330b778b777385fcba384a1a02 failed

Thank you for this library!

I plugged it in today and got it running by simply copying the README example. Compared to redux-persist it's very easy to setup.

Sadly I ran into an issue with our CI/CD which restricts NPM packages to be published on a registry. Meaning; external paths are blocked.

The lz-string dependency from this lib gets pulled-in through using:

"lz-string": "github:pieroxy/lz-string#b2e0b27",

I don't know why this library needed this specific version, I checked the git history but couldn't find a cause. One reason might be that the lz-string lib hasn't been published for a couple of years (this is just a guess):

pieroxy/lz-string#125

Having the github in package.json instead of normal semver has two consequences for my project:

  • lz-string was already a dependency for several other packages all using the latest published v1.4.4. Now it's pulled-in a second time I think.
  • our CI/CD won't download it

I have found a workaround by using force-resolution; https://www.npmjs.com/package/npm-force-resolutions

That workaround is a bit of a last-resort tbh. I've turned compression off which effectively makes lz-string an optional dependency I think. It's unlikely I'll ever hit the cookie limit for our use-case.

I'm not sure how this can be resolved now that dependents of this library use this unreleased version of lz-string. The only idea I had was keeping the lz-string dep as-is but change it to an optional dependency so people who don't need it can opt to exclude it (https://docs.npmjs.com/cli/v8/configuring-npm/package-json#optionaldependencies).

I figured I'd open this issue if others run into the same issue.

Ability to hydrate all pages without getServerSideProps and connect.

I opened this in the next-redux-wrapper issues but it might be more appropriate here. I use Redux for saving my current theme I use in Material UI. They use a wrapper that provides the theme and as such I have a component that uses a selector to get the current theme and apply it. The issue is that I need to hydrate all pages for it to work meaning I have to create lots of empty getServerSIdeProps functions and connect pages which leads to alot of boilerplate. I would like the ability to turn on hydration for all pages regardless of if it is connected or not.

Small question

Hello!
Can you help me with some problem?
How I can implement several redux persist for different object in store.
For example:
I have this tree structure:

store: {
     user: {
        token: ... ,
        name: ...
     },
     global: {
        theme: ....
     }
}

I want split on module, and in every module set:
global module: image
user module:
image
Can you help me, or share example (without react-toolkit)
Many thanks!:)

Getting error: TypeError: store.getState is not a function

Followed the docs and updated my code from:

const bindMiddleware = (middleware) => {
  if (process.env.NODE_ENV !== 'production') {
    const { composeWithDevTools } = require('redux-devtools-extension')
    return composeWithDevTools(applyMiddleware(...middleware))
  }
  return applyMiddleware(...middleware)
}
...
const initStore = () =>  createStore(reducer, bindMiddleware([thunkMiddleware]))

export const wrapper = createWrapper(initStore)

to:

const bindMiddleware = (middleware) => {
  if (process.env.NODE_ENV !== 'production') {
    const { composeWithDevTools } = require('redux-devtools-extension')
    return composeWithDevTools(applyMiddleware(...middleware))
  }
  return applyMiddleware(...middleware)
}
...
const initStore = () => wrapMakeStore(() =>
  createStore(reducer, bindMiddleware([thunkMiddleware, nextReduxCookieMiddleware({ subtrees: ["auth"] })]))
)

export const wrapper = createWrapper(initStore)

The code works as expected before making the change; however, after the change, I get the following error: TypeError: store.getState is not a function. How can this be resolved?

Client build includes server-only libraries

I noticed that when I build a project including this library, the resulting client build includes a bunch of server libraries that bloat up the size.
For comparison, here's the output of a clean create-next-app output:

Page                                                           Size     First Load JS
┌ λ /                                                          7.08 kB        69.8 kB
├   /_app                                                      4.67 kB        62.7 kB
└ λ /404                                                       3.25 kB          66 kB
+ First Load JS shared by all                                  62.7 kB
  ├ static/pages/_app.js                                       4.67 kB
  ├ chunks/fa0d1538d4ec6ce8974fb6c1dd3701ae265e0f01.b01e96.js  10.5 kB
  ├ chunks/framework.c8d289.js                                 40.3 kB
  ├ runtime/main.71488f.js                                     6.54 kB
  └ runtime/webpack.c21266.js                                  746 B

After adding this library, here's the resulting output size:

Page                                                           Size     First Load JS
┌ λ /                                                          7.08 kB         211 kB
├   /_app                                                      145 kB          203 kB
└ λ /404                                                       3.25 kB         207 kB
+ First Load JS shared by all                                  203 kB
  ├ static/pages/_app.js                                       145 kB
  ├ chunks/e3159650bc6aa89eb4037de31e151c2fac446e71.c2e7b8.js  10.7 kB
  ├ chunks/framework.c8d289.js                                 40.3 kB
  ├ runtime/main.92af30.js                                     6.28 kB
  └ runtime/webpack.c21266.js                                  746 B

Here's an analysis of the client output with this library:
image

It seems all of the extraneous libraries stem from the importing of cookies.
If I dynamically import this library, the size goes back down to something reasonable (the analysis diagram still ends up including the extraneous libraries, although they're not loaded):

ServerCookies = (await import ('cookies')).default as any
cookies = new NodeCookiesWrapper(new ServerCookies(req, res));
Page                                                           Size     First Load JS
┌ λ /                                                          7.08 kB        72.2 kB
├   /_app                                                      6.63 kB        65.1 kB
└ λ /404                                                       3.25 kB        68.3 kB
+ First Load JS shared by all                                  65.1 kB
  ├ static/pages/_app.js                                       6.63 kB
  ├ chunks/971fcca33ddeb824ee6f37f20ba5b5cb1789aea2.c2e7b8.js  10.7 kB
  ├ chunks/framework.c8d289.js                                 40.3 kB
  ├ runtime/main.92af30.js                                     6.28 kB
  └ runtime/webpack.0b6f2e.js                                  1.18 kB

This is probably not the ideal solution, but just throwing that out there.

Properties removed if not included in subtrees.

Hi,

I've run into the scenario that I want to save part of my state in the cookies, but don't care about the rest.
My state "cart" contains PRODUCTS, ITEMS and TOTAL and I only want to "persist" ITEMS.

My issue now is that the PRODUCTS and TOTAL properties get removed competely on HYDRATE, as shown in the image here:
Image of State

This is my code:

const rootReducer = (state, action) => {
	switch (action.type) {
		case HYDRATE:
			return {
				...state,
				...action.payload
			}
		default:
			return combinedReducers(state, action);
	}
};

const makeStore = wrapMakeStore(() =>
	createStore(
		rootReducer,
		composeWithDevTools(
			applyMiddleware(
				nextReduxCookieMiddleware({
					subtrees: ["cart.ITEMS"],
					maxAge: 1209600000
				}),
				thunkMiddleware
			)
		)
	)
);

Is this just me not implementing proper state reconciliation? If it is, could you provide an example?

SSR store missing cookie data in Next 13

Hey there, I've copied the example code identically but using next@^13, and I'm getting an hydration mismatch. It looks like my store is being initialized empty despite the cookies existing:

4. useWrappedStore created new store with { initialState: undefined, initialStateFromGSPorGSSR: undefined

Were there any breaking changes in 13?

State from Cookies not set back after browser reload

Hi sir @bjoluc. I hope you are well,

Sorry in advance. I have a problem, i.e. my app.theme state is not set from the state already stored in cookies. but returns to initial value when browser is reloaded. I don't know if there is an error in my next-redux-wrapper or next-redux-cookie-wrapper code. I attach a video recording and some source code of the problem I found

Stack

  • next-redux-wrapper
  • next-redux-cookie-wrapper
  • without redux-toolkit
msedge_a2laH6MNW0.mp4

AppReducers

import { AppActionType } from 'global/action-types';
import { HYDRATE } from 'next-redux-wrapper';
import { AnyAction } from 'redux';

export enum ThemeAppEnum {
  LIGHT = 'LIGHT',
  DARK = 'DARK',
}

interface State {
  theme: ThemeAppEnum;
  locale: string | null;
  sidebar: boolean;
  server?: any;
  client?: any;
}

const initialState: State = {
  theme: ThemeAppEnum.LIGHT,
  locale: null,
  sidebar: true,
};

const reducer = (state: State = initialState, action: AnyAction) => {
  switch (action.type) {
    case HYDRATE:
      return {
        ...state,
        server: {
          ...state.server,
          ...action.payload.server,
        },
      };
    case 'SERVER_ACTION':
      return {
        ...state,
        server: {
          ...state.server,
        },
      };
    case 'CLIENT_ACTION':
      return {
        ...state,
        client: {
          ...state.client,
        },
      };
    case AppActionType.UPDATE_LOCALE_APP:
      return {
        ...state,
        theme: action.payload,
      };
    case AppActionType.UPDATE_SIDEBAR_COLLAPSED_APP:
      return {
        ...state,
        sidebar: action.payload,
      };
    case AppActionType.UPDATE_THEME_APP:
      return {
        ...state,
        theme: action.payload,
      };
    default:
      return state;
  }
};

export default reducer;

Store

import { createStore, applyMiddleware } from 'redux';
import { createWrapper } from 'next-redux-wrapper';
import {
  nextReduxCookieMiddleware,
  wrapMakeStore,
} from 'next-redux-cookie-wrapper';
import { createLogger } from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';
import reducer from './reducers';
import thunkMiddleware from 'redux-thunk';

const bindMiddleware = (middleware: any) => {
  if (process.env.NODE_ENV !== 'production') {
    return composeWithDevTools(applyMiddleware(...middleware));
  }

  return applyMiddleware(...middleware);
};

const logger = createLogger({ diff: true });

const makeStore = wrapMakeStore(() =>
  createStore(
    reducer,
    bindMiddleware([
      nextReduxCookieMiddleware({
        subtrees: [
          {
            subtree: 'app.theme',
            compress: false,
          },
        ],
      }),
      logger,
      thunkMiddleware,
    ])
  )
);

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

Sorry if my question is silly, but I'm really confused where is the wrong part. Thank you in advance.

Not working

Hi, i'm using next-redux-cookie-wrapper on a project using also next-seo and next-auth.
my _app.js is like

<SessionProvider
      // Provider options are not required but can be useful in situations where
      // you have a short session maxAge time. Shown here with default values.
      options={{
        // Stale Time controls how often the useSession in the client should
        // contact the server to sync the session state. Value in seconds.
        // e.g.
        // * 0  - Disabled (always use cache value)
        // * 60 - Sync session state with server if it's older than 60 seconds
        staleTime: 0,
        // Refetch Interval tells windows / tabs that are signed in to keep sending
        // a keep alive request (which extends the current session expiry) to
        // prevent sessions in open windows from expiring. Value in seconds.
        //
        // Note: If a session has expired when keep alive is triggered, all open
        // windows / tabs will be updated to reflect the user is signed out.
        refetchInterval: 0
      }}
      session={session}
    >
      {/* <Provider store={store}> */}
      <Layout {...Component.layoutProps}>
        <DefaultSeo {...SEO} />
        <Component {...pageProps} />
      </Layout>
      {/* </Provider> */}
    </SessionProvider>

The cookie is written in browser but i'm unable to reload the page retaining the data.
Any idea?

Hydrate state from cookies without server side rendering

Hi! I'm trying to implement this library, and it works great, when the page has some SSR methods (like getServerSideProps or others), however, I am missing one important function - it can't hydrate the state from cookies when the page doesn't have SSR.

So I have some state in cookies, and it hydrates perfectly when page has gSSP method. However, when there are no SSR methods on page, next-redux-wrapper doesn't call HYDRATE action and the state is not fetched from cookies.

My question is: can I hydrate the state from cookies on client-side without using gSSP or other server-side rendering methods?

Expose functions to manually parse cookies

@bjoluc I was thinking that it would be nice if we can expose decompressFromEncodedURIComponent so that we don't need to install lz-string on our project to decode the cookie data. Is this possible ?

Originally posted by @bryantobing12 in #17 (comment)

@bryantobing12 Thanks, agree! I'd like to have two functions, one for compressed and one for non-compressed cookies. I'd also include JSON.parse in the exposed methods, unless there's a reason not to do so?

Cookies not being stored properly? state undefined

I followed the installation instructions but it is not working. When I refresh page after changing redux state, the state is lost.

I only have one additional cookie with name="reduxPersistIndex" and value="[%22persist:root%22]". This does not look right?

The 1. 3. and 4. log outputs from debug mode look OK to me. This is expected since next-redux-wrapper is working fine, and these are logs from that. However the additional logs seem to indicate something is wrong.

0. Extracting state from cookies (if any)
0. Got state undefined

Any idea why it would be coming back undefined?

Snippet from my _app.js:

MyApp.getInitialProps = async ({ Component, ctx}) => {
  const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
  return {pageProps};
}

export default withReduxCookiePersist(makeStore)(MyApp);

versions:
next @9.2.1
next-redux-cookie-wrapper @1.0.1
redux @4..0.4

If additional information is needed please let me know...

Thanks!

Store Value Stored In Cookies Defaulting To Store Initial Value When Redirected From An External Url

Hello i have been trying to debug this issues for days now and right now i really need help, I'm building a fintech app where the user is redirected to an external url to add their card, when the user is done i provided a redirect url to the external source to redirect the user back to my website.

Now here is the problem after some investigation on my end, when ever the user get redirected from the external source, the token and other store values that was stored in the browser cookie and meant to be persisted all gets reset to the default value i used while setting up their initial state in their individual reduxtoolkit Slice. so by the time the user gets redirected back to my website, token would have had its value reset to "null" which was the default value i set in my authSlice, prompting the user to login again

This is my full package.json file
{
"name": "more360-web",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"test": "jest --watch",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@mapbox/mapbox-gl-geocoder": "^5.0.1",
"@reduxjs/toolkit": "^1.8.1",
"@tailwindcss/line-clamp": "^0.4.0",
"@tanem/react-nprogress": "^5.0.10",
"antd": "^4.20.2",
"axios": "^0.27.2",
"gsap": "^3.10.4",
"jest-environment-jsdom": "^28.0.2",
"mapbox-gl": "^2.8.2",
"moment": "^2.29.4",
"next": "12.1.6",
"next-axiom": "^0.14.0",
"next-plugin-antd-less": "^1.8.0",
"next-redux-cookie-wrapper": "^2.2.1",
"next-redux-wrapper": "^8.0.0-rc.1",
"react": "18.1.0",
"react-animate-height": "^2.1.2",
"react-dom": "18.1.0",
"react-redux": "^8.0.2",
"react-reveal": "^1.2.2",
"react-scroll-to-top": "^1.0.8",
"react-toastify": "^9.0.5",
"uuid": "^8.3.2"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.2",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1",
"autoprefixer": "^10.4.7",
"babel-plugin-import": "^1.13.5",
"eslint": "8.14.0",
"eslint-config-next": "12.1.6",
"jest": "^28.0.3",
"postcss": "^8.4.13",
"tailwindcss": "^3.0.24"
}
}

The App was hosted on vercel tho

PC spec
Hp elite book window 10

Bug: "Cannot set headers after they are sent to the client"

Hey all, anyone come across this issue? Not sure how to reproduce / where to start

listenerMiddleware/error Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at new NodeError (node:internal/errors:372:5)
    at Proxy.setHeader (node:_http_outgoing:576:11)
    at Object.setCookie (/Users/username/project/node_modules/nookies/dist/index.js:89:17)
    at StateCookies.set (/Users/username/project/node_modules/next-redux-cookie-wrapper/dist/next-redux-cookie-wrapper.cjs.development.js:217:13)
    at /Users/username/project/node_modules/next-redux-cookie-wrapper/dist/next-redux-cookie-wrapper.cjs.development.js:386:29
    at /Users/username/project/node_modules/next-redux-cookie-wrapper/dist/next-redux-cookie-wrapper.cjs.development.js:250:22
    at Immer.produce (/Users/username/project/node_modules/immer/dist/immer.cjs.development.js:792:20)
    at walkState (/Users/username/project/node_modules/next-redux-cookie-wrapper/dist/next-redux-cookie-wrapper.cjs.development.js:246:11)
    at /Users/username/project/node_modules/next-redux-cookie-wrapper/dist/next-redux-cookie-wrapper.cjs.development.js:382:17
    at /Users/username/project/node_modules/@reduxjs/toolkit/dist/redux-toolkit.cjs.development.js:1792:22 {
  code: 'ERR_HTTP_HEADERS_SENT'
} { raisedBy: 'effect' }

__NEXT_REDUX_WRAPPER_HYDRATE__ cookie bug

website eg:www.example.com
users:User A and User B
action:User A login example website and can get cookie,then User B with other PC open browser run example website User A login info appear in User B browser!!!
image

Add example project

I would love to see example project using this library, it would be very helpful for newcomers (like me :D)

Next 9.5 Upgrade

Next-Redux has been changed. SSG and SSR methods compatibility.

Does not work well with axios timeout functionality

When using this with axios's timeout functionality on a request, there seems to be a bug that breaks the cookie storage of the state after the HTTP request made by axios with the timeout request. I'll add some code to show what I mean.

const aFunction = () => async dispatch => {
    dispatch({
        type: 'Action 1'
     });
     axios.get('url', { timeout: 10000 });
     dispatch({
         type: 'Action 2'
     });

The state change caused by Action 2 will not be present when the application is reloaded or revisited, it will use Action 1's state changes as the last state changes made and store those to the cookies.

middleware sometime can't get subtrees

store.js

import {axiosMiddleware} from './axiosMiddleware'


export const initStore = () => configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(
    nextReduxCookieMiddleware({
      // compress: true,
      subtrees: ["auth.accessToken", "auth.refreshToken", "auth.isLogin", "auth.me"],
    })
  ).concat(axiosMiddleware)
})

axiosMiddleware.js

export const axiosMiddleware = (store) => (next) => (action) => {
  createAuthRefreshInterceptor(axiosInstance, async failedRequest => {
    console.log('failedRequest=', store.getState().auth)
    const res = await store.dispatch(refreshToken());
    if (res.payload && res.payload.accessToken){
      const bearer = `Bearer ${res.payload.accessToken}`
      axiosInstance.defaults.headers.Authorization = bearer
      failedRequest.response.config.headers.Authorization = bearer
      return Promise.resolve();
    }else{
      return Promise.reject()
    }
  });
  return next(action);
}

look at console.log('failedRequest=', store.getState().auth) sometime I can get the auth state ,but sometime I cant get it why???

Problem of hydration from the cookie data

Hi,

I am facing an issue on a personal project. I am calling an API to have my data into getServerSideProps in the index.tsx.
My goal is to save the data into a cookie so that I wouldn't have to call the API each time the user reload the page.

Thing is, I am all the time facing this error on page load : Error: Hydration failed because the initial UI does not match what was rendered on the server.

It is like the data retrieved from the api call was cleaned and deleted from the store after page load on the SSR...
I mean, the hydration from the data received during SSR are not set in the CSR store

I am new to Nextjs and I would love to have your help :)

package.json
Capture d’écran 2023-03-17 à 09 19 39

store.ts
Capture d’écran 2023-03-17 à 09 22 29

index.tsx
Capture d’écran 2023-03-17 à 09 23 20

fishesSlice.ts
Capture d’écran 2023-03-17 à 09 21 18

Sync client state with cookies

Is there any way to sync the client state with the state defined in the cookies? Currently it is only set/updated during SSR.

This could be useful if you wanted to skip fetching certain data that has to be fetched on the client by keeping it in the state and will be already available on page load.

Changing compress config to false throws 'SyntaxError: Unexpected token N in JSON at position 0' error

Hello, First of all thanks a lot for this amazing wrapper.
I need to persist cookie without compression, but when i change compress config to false bellow error happens:

SyntaxError: Unexpected token N in JSON at position 0
at JSON.parse ()
at Function._decodeState (/home/rezasohrabi/Desktop/node_modules/next-redux-cookie-wrapper/dist/next-redux-cookie-wrapper.cjs.development.js:170:17)
at StateCookies.getAll (/home/rezasohrabi/Desktop/node_modules/next-redux-cookie-wrapper/dist/next-redux-cookie-wrapper.cjs.development.js:204:46)
at Object.dispatch (/home/rezasohrabi/Desktop/node_modules/next-redux-cookie-wrapper/dist/next-redux-cookie-wrapper.cjs.development.js:332:40)

I am persisting user object, here is my wrapped store codes:

const makeStore = wrapMakeStore(() =>
  configureStore({
    reducer: rootReducer,
    middleware: (getDefaultMiddleware) =>
      getDefaultMiddleware()
        .prepend(
          nextReduxCookieMiddleware({
            subtrees: [
              {
                cookieName: 'userV3',
                subtree: 'auth.user',
                compress: false,
              },
            ],
          })
        )
        .concat(logger as ReturnType<typeof getDefaultMiddleware>),
  })
)

how set refresh token in cookie

What version of Next.js are you using?

"next": "latest",

What version of Node.js are you using?

v14.17.4

What browser are you using?

Chrome

What operating system are you using?

MacOS

How are you deploying your application?

yarn dev

Describe the Bug

when my token is expired so I refresh token , so console this not work!!!

warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
Not setting "auth.accessToken" cookie. Response has finished.
You should set cookie before res.send()
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
Not setting "auth.refreshToken" cookie. Response has finished.
You should set cookie before res.send()
error= Error: Request failed with status code 401

setupIntersetor.js

import axiosInstance from "./axios";
import createAuthRefreshInterceptor from 'axios-auth-refresh';
import qs from 'qs'
import {setAuth} from '@/store/slices/authSlice'

const setup = (store) => {
  console.log('---------setup store--------', JSON.stringify(store.getState().auth))
  const {dispatch} = store;

  createAuthRefreshInterceptor(axiosInstance, async failedRequest => {
    const authState = store.getState().auth;
    console.log('-------------- failedRequest222 authState -------------', JSON.stringify(authState))
    if (authState.refreshToken) {
      return await axiosInstance.post('/auth/oauth/token',
        qs.stringify({
          grant_type: 'refresh_token',
          refresh_token: authState.refreshToken
        })).then(async tokenRefreshResponse => {
        console.log('------------获取新token了-----------', tokenRefreshResponse)
        failedRequest.response.config.headers.Authorization = 'Bearer ' + tokenRefreshResponse.data.access_token;
        await dispatch(setAuth({
          accessToken: tokenRefreshResponse.data.access_token,
          refreshToken: tokenRefreshResponse.data.refresh_token,
          isLogin: true,
          isExpired: false,
        }))
        return Promise.resolve();
      })
    } else {
      return Promise.resolve();
    }

  });
}

export default setup;

store/slices/authSlice

setAuth(state, action) {
      console.log('-----------setAuth-----------', action)
      if(action.payload){
        state.accessToken = action.payload.accessToken;
        state.refreshToken = action.payload.refreshToken;
        state.isLogin = action.payload.isLogin;
        state.isExpired = action.payload.isExpired;
      }
    }

store.js

import setupInterceptors from "./setupInterceptors";

const combinedReducers = combineReducers({
  [authSlice.name]: authSlice.reducer,
  [layoutSlice.name]: layoutSlice.reducer,
  [systemSlice.name]: systemSlice.reducer,
  [spaceSlice.name]: spaceSlice.reducer,
  [settingSlice.name]: settingSlice.reducer,
  [userSlice.name]: userSlice.reducer,
  [homeSlice.name]: homeSlice.reducer
});
export const store = wrapMakeStore(() => {
  const initStore = configureStore({
    reducer: combinedReducers,
    middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(
      nextReduxCookieMiddleware({
        subtrees: ["auth.accessToken", "auth.refreshToken", "auth.isLogin", "auth.me"],
      })
    )
  });
  setupInterceptors(initStore);
  return initStore;
});

const makeStore = () => store;

export const wrapper = createWrapper(store, {storeKey: 'key', debug: true});

Any way to retrieve cookie on server side?

First of all, this is a great wrapper. Many thanks for putting effort.
Any way to retrieve cookie on server side except req.headers.cookie ?
I'm using nextJS's new getServerSideProps function.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.