# No BS Next with Redux implementation on SSR

Let me save you some precious time, because I've spent many hours and 
[brain cells](https://github.com/kirill-konshin/next-redux-wrapper/issues/457) researching this subject. 

First things firs, you will need [NextJs](https://nextjs.org/docs), [Redux Toolkit](https://redux-toolkit.js.org/introduction/getting-started) and [Next Redux Wrapper](https://github.com/kirill-konshin/next-redux-wrapper)

If you want a video, then this is for you:
<iframe width="560" height="315" src="https://www.youtube.com/embed/pstQ7xyg8p8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

    npx create-next-app@latest --typescript

    npm install @reduxjs/toolkit --save

    npm install next-redux-wrapper react-redux --save

    
We'll work on a project structure like this one

![ScreenHunter 177.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1644518246850/8wmsOkxmc.png)

In store/index.ts you will have the store configuration

    import { configureStore, ThunkAction } from '@reduxjs/toolkit';
    import { createWrapper } from 'next-redux-wrapper';
    import { Action } from 'redux';
    import profileReducer from './slices/profile';
    import productReducer from './slices/product';

    const makeStore = () => configureStore({
      reducer: {
        profile: profileReducer,
        product: productReducer
      },
      devTools: true
    });

    export type AppStore = ReturnType<typeof makeStore>;
    export type AppState = ReturnType<AppStore['getState']>;
    export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, AppState, 
    unknown, Action>;

    export const wrapper = createWrapper<AppStore>(makeStore);


In store/slices/profile.ts
- The HYDRATE action is important as it makes sure the state is passed from the server to the client side on refresh
- All pages with getServerSideProps call the HYDRATION action on the client as well; This means that you need to make sure you don't override the client state with empty data, by checking if  you have data in the action payload. More about this, [here](https://github.com/kirill-konshin/next-redux-wrapper);


    import { createSlice } from '@reduxjs/toolkit';
    import { HYDRATE } from 'next-redux-wrapper';
    import { AppState, AppThunk } from '..';

    export const ProfileSlice = createSlice({
      name: 'profile',

      initialState: {
        name: null
      },

      reducers: {
        setProfileData: (state, action) => {
          state.name = action.payload;
        }
      },

      extraReducers: {
        [HYDRATE]: (state, action) => {  // IMPORTANT - for server side hydration
         
          if (!action.payload.profile.name) {  // IMPORTANT - for not overriding data on client side
            return state;
          }

          state.name = action.payload.profile.name;
        }
      }
    });

    export const { setProfileData } = ProfileSlice.actions;

    export const selectProfile = (state: AppState) => state.profile;

    export default ProfileSlice.reducer;

In store/slices/product.ts

    import { createSlice } from '@reduxjs/toolkit';
    import { HYDRATE } from 'next-redux-wrapper';
    import { AppState, AppThunk } from '..';

    export const ProductSlice= createSlice({
      name: 'product',

      initialState: {
        name: null
      },

      reducers: {
        setProductData: (state, action) => {
          state.name = action.payload;
        }
      },

      extraReducers: {
        [HYDRATE]: (state, action) => {
          console.log('HYDRATE', action.payload);

          if (!action.payload.product.name) {
            return state;
          }

          state.name = action.payload.product.name;
        }
      }
    });

    export const { setProductData } = ProductSlice.actions;

    export const selectProduct = (state: AppState) => state.product;

    // You can do async http calls with thunks
    export const fetchProduct =
        (): AppThunk =>
          async dispatch => {
            const timeoutPromise = (timeout: number) => new Promise(resolve => 
    setTimeout(resolve, timeout));

            await timeoutPromise(1000);

            dispatch(
              setProductData('BA DUM DA THUNK')
            );
          };


    export default ProductSlice.reducer;


In _app.tsx 

    import '../styles/styles.scss';
    import type { AppProps } from 'next/app';
    import { wrapper } from 'app/store';

    function MyApp({ Component, pageProps }: AppProps) {
      return <Component {...pageProps} />;
    }

    export default wrapper.withRedux(MyApp);

In pages/index.tsx

    import type { NextPage } from 'next'
    import Head from 'next/head'
    import Image from 'next/image'
    import { useSelector } from 'react-redux'
    import Link from 'next/link'

    import { wrapper } from 'app/store'
    import styles from '../styles/Home.module.css'
    import { selectProfile, setProfileData } from 'app/store/slices/profile'

    const Home: NextPage = (props: any) => {
      const { resolvedUrl } = props;
      const profile = useSelector(selectProfile);

      return (
        <div className={styles.container}>
          <Head>
            <title>Create Next App</title>
            <meta name="description" content="Generated by create next app" />
            <link rel="icon" href="/favicon.ico" />
          </Head>

          <main className={styles.main}>
            <h1 className={styles.title}>
              Welcome to <a href="https://nextjs.org">Next.js!</a>
              { resolvedUrl }
            </h1>
            <h2>This is the redux data: {profile.name}</h2>

            <p>
              Navigate to <Link href="/profile"><a >Profile</a></Link>
            </p>
          </main>
        </div>
      )
    }

    export const getServerSideProps = wrapper.getServerSideProps(store => async 
    ({resolvedUrl}) => {
      console.log('resolvedUrl ', resolvedUrl);

      store.dispatch(setProfileData('My Server Name'))

      return {
        props: {
          resolvedUrl
       }
      }
    })

    export default Home

In pages/profile.tsx

Mostly the same as in index.tsx, ecept

    import { useSelector } from 'react-redux'

    import { wrapper } from 'app/store'
    import { selectProfile } from 'app/store/slices/profile'

    const Profile: NextPage = (props: any) => {
      const profile = useSelector(selectProfile);

      return (
        <div className={styles.container}>
          <main className={styles.main}>
            <h2>This is the redux data set in index: {profile.name}</h2>
            <p>
              Navigate to <Link href="/"><a >Home</a></Link> {' | '} <Link href="/product"> <a >Product</a></Link>
            </p>
          </main>
        </div>
      )
    }

    export const getServerSideProps = wrapper.getServerSideProps(store => async () => {

      return {
        props: {}
      }
    })

    export default Profile


In pages/product.tsx

Mostly the same as in index, except you can use connect to select the data from the store; You map the state to the component props.

You also dispatch the thunks as you would dispatch any action; Just don't forget to await for the dispatch.


    import { connect } from 'react-redux';

    import { AppState, wrapper } from 'app/store';
    import { fetchProduct } from 'app/store/slices/product';


    const Product: NextPage = (props: any) => {
      const { product, profile } = props;

      return(<div>{product?.name}, {profile?.name}</div>)
    }

    export const getServerSideProps = wrapper.getServerSideProps(store => async () => {
      await store.dispatch(fetchProduct());

      return {
        props: {}
      };
    });

    const mapStateToProps = (state: AppState) => ({
      profile: state.profile,
      product: state.product
    });

    export default connect(mapStateToProps)(Product);

That's it! Find a project implementation on my [github](https://github.com/mmswi/nextjs-starter).


