Giter Club home page Giter Club logo

with-next-redux-wrapper-redux-persist's Introduction

✅ Update: 2.0.0 is launched with major updates. All packages are updated to the latest version. React 17+ and Next 11+ is added. Redux persist storage is reorganized. Everything is tested. Thanks for your overwhelming love and support. ✌

Next Redux Wrapper along with Redux Persist (& redux-thunk too) - A ready boilerplate for NEXT JS projects with persistent global state ✨

I just needed a simple persistent global state for my Next.js website. This is a redux boilerplate and I wrote it for myself at first to speed up my future project. Now I added extensive comment to help people understanding the whole implementation.

Using a persistent global state in a Next js website is much more daunting than I thought! See my angry tweet 😂. You have to have a solid understanding of client-side and server-side rendering & how Next js is dealing with it! Obviously don't forget about the persistency feature and some performance drawbacks in the case of server-side rendering. So I created this boilerplate containing a classic counter app example.

Counter Example

Why this boileplate? 😰

Copy and pasting redux boilerplate code without understanding seems a norm to people who are relatively new to the redux ecosystem. If you are new to redux, you may use this boilerplate to copy and paste and attain the functionality right away. Okay, you can do this. But it will be more helpful if you understand the code and implement it by yourself. You may take help from this boilerplate code. I didn't find a boilerplate like this in the next js example section. Besides I used hook every time in my redux code. The example in the next js repo weren't written with the hooks. Hooks in redux are elegant and powerful.😍

So I decided to write one for myself!

Enough Talk, Show me the code

What should be the structure of your redux store?

// ./store/store
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { createWrapper, HYDRATE } from 'next-redux-wrapper';
import thunkMiddleware from 'redux-thunk';
import counter from './counter/reducer';
import storage from './sync_storage';
// If you don't bother about the error redux-persist failed to create sync storage. falling back to noop storage...uncomment the next line and comment out the previous import. See more on - https://github.com/vercel/next.js/discussions/15687
// const storage = require('redux-persist/lib/storage').default;

//COMBINING ALL REDUCERS
const combinedReducer = combineReducers({
  counter,
  // OTHER REDUCERS WILL BE ADDED HERE
});

// BINDING MIDDLEWARE
const bindMiddleware = (middleware) => {
  if (process.env.NODE_ENV !== 'production') {
    const { composeWithDevTools } = require('redux-devtools-extension');
    return composeWithDevTools(applyMiddleware(...middleware));
  }
  return applyMiddleware(...middleware);
};

const makeStore = ({ isServer }) => {
  if (isServer) {
    //If it's on server side, create a store
    return createStore(combinedReducer, bindMiddleware([thunkMiddleware]));
  } else {
    //If it's on client side, create a store which will persist
    const { persistStore, persistReducer } = require('redux-persist');

    const persistConfig = {
      key: 'nextjs',
      whitelist: ['counter'], // only counter will be persisted, add other reducers if needed
      storage, // if needed, use a safer storage
    };

    const persistedReducer = persistReducer(persistConfig, combinedReducer); // Create a new reducer with our existing reducer

    const store = createStore(
      persistedReducer,
      bindMiddleware([thunkMiddleware])
    ); // Creating the store again

    store.__persistor = persistStore(store); // This creates a persistor object & push that persisted object to .__persistor, so that we can avail the persistability feature

    return store;
  }
};

// Export the wrapper & wrap the pages/_app.js with this wrapper only
export const wrapper = createWrapper(makeStore);

View the file structure along with the code

Code with the file structure

How you should wrap the _app js file

Screenshot for understanding the file structure along with the code.

Code with the file structure

External API call & NEXT RUN BUILD

I tried to add some external API call to see the hydration and rehydration process. Have a look into the SSG, Static & SSR rendering of the pages in this boilerplate. Seems good to me.

Code with the file structure

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

License

MIT

with-next-redux-wrapper-redux-persist's People

Contributors

dependabot[bot] avatar fazlulkarimweb avatar sidneypp 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

with-next-redux-wrapper-redux-persist's Issues

redux-persist failed to create sync storage. falling back to noop storage.

If i use PersistGate in the _app.js like the following manner then the terminal says "redux-persist failed to create sync storage. falling back to noop storage."

        <PersistGate loading={<div>loading</div>} persistor={store.__persistor}>
          <Component {...pageProps} />
        </PersistGate>

However it's easy to fix :

      <Provider` store={store}>
        <PersistGate loading={<div>loading</div>} persistor={store.__persistor}>
          <Component {...pageProps} />
        </PersistGate>
      </Provider>

And here is the files i am importing in the _app.js

    import App from 'next/app';
    import React from 'react';
    import { Provider,useStore } from 'react-redux'
    import { wrapper } from '../store/store'
    import { PersistGate } from 'redux-persist/integration/react'

Leveraging persistor

Thanks for your boilerplate, it's helped me quite a bit.

One thing I'm not certain of is how to leverage the persistor manually.

It's applied as a key onto the store object that's return part of the makeStore method like so: store.__persistor.

I ended up doing this in my _app instead:

const store = useStore();
const persistor = persistStore(store);

but both of our solutions don't provide access to the __persistor value when I want to do something like persistor.flush().

Any ideas?

This config removes SSR data

As we we know, most of the bleeding of using NextJS is using it's SSR feature to handle SEO.
But this config removes data on start and shows a loading... of Redux persistor.

What is expected?
Show real data instead of loading when you open a page at first.

Use with typescript

When i use this with typescript i need to add any type everywhere? could you please add an example with typescript ?

How to get current state in getServerSideProps

i want get current state in getServerSideProps like this:

export const getServerSideProps = wrapper.getServerSideProps(
  (store) => async (context) => {
  let {counter} = store.getState();
    const data = await fetchData();
    return { props: data };
  }
);

But it always return initialState:

{ server: '', client: '', counter: 0 }

My code in reducer:

  switch (action.type) {
    case INCREMENT_COUNTER:
      return { ...state, counter: action.payload };
    case DECREMENT_COUNTER:
      return { ...state, counter: action.payload };
    case HYDRATE:
      return { ...state, ...action.payload.counter };
    default:
      return state;
  }
}

How can do that?

store.dispatch is not a function

Hello, my code is returning this flaw mentioned in the title, I did it according to the README model, but it's as if the store was in trouble. Can anyone give a hand?

store/index.js

import { createStore, combineReducers } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import { createWrapper } from 'next-redux-wrapper';
import storage from 'redux-persist/lib/storage';

import modal from '../reducers/modal';
import cart from '../reducers/cart';

const reducer = combineReducers({
  modal,
  cart,
});

const makeConfiguredStore = (reducer) =>
  createStore(reducer, undefined);

const makeStore = () => {
  const isServer = typeof window === 'undefined';

  if (isServer) {
    return makeConfiguredStore(reducer);
  }
  
  const persistConfig = {
    key: 'root',
    storage,
  };

  const persistedReducer = persistReducer(persistConfig, reducer);
  const store = makeConfiguredStore(persistedReducer);
  store.__persistor = persistStore(store);

  return store;
};

const persistor = persistStore(makeStore);

const wrapper = createWrapper(makeStore);

export {
  persistor,
  wrapper,
}

_app.js

import App from 'next/app';
import { ReactReduxContext } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react';
import { ThemeProvider } from 'styled-components';

import { wrapper } from '../store';
import baseTheme from '../utils/theme';

class MainApp extends App {
	render() {
		const { Component, pageProps } = this.props;
		return (
			<ReactReduxContext.Consumer>
				{({ store }) => {
					<PersistGate persistor={store.__persistor} loading={null}>
						<ThemeProvider theme={baseTheme}>
							<Component {...pageProps} />
						</ThemeProvider>
					</PersistGate>
				}}
			</ReactReduxContext.Consumer>
		);
	}
}

export default wrapper.withRedux(MainApp);

Unfortunately this repository is not SEO friendly

As reported in this issue: #8

The main issue with using Redux and Redux-persist with Next.js is still not solved.

Steps to reproduce:

  1. Download this repo
  2. Run npm install && npm run dev
  3. Open chrome and disable Javascript in the browser (or view source)
  4. See a white screen (no content loaded == not good for SEO)

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.