reactjsreact-reduxhigher-order-componentsnext-redux-wrapper

How to Move Next-Redux-Wrapper Store Provider to Layout Component?


Can you please help me move the next-redux-wrapper store provider into a layout component?

My current codes follows the next-redux-wrapper office docs and it works fine, but I would like to move the store provider into a layout component in case where the Redux store provider isn't required as it might just be a plain page.

When I tried to move it to a layout component, I'm not able to access the pageProps as props is now a jsx element. But the parent pageProps is required by next-redux-wrapper.

I don't really know how to do this. How do I get the original parent pageProps in the layout component?

Here is my working version:

//_app.tsx

import { NextPage } from "next";
import type { AppProps } from "next/app";
import { ReactElement, ReactNode } from "react";
import { Provider } from "react-redux";
import { wrapper } from "../../lib/redux/store/store";
import "../../styles/globals.css";

type NextPageWithLayoutAndAuth = NextPage & {
  getLayout?: (page: ReactElement) => ReactNode;
  auth?: boolean;
};

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayoutAndAuth;
};

function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout = Component.getLayout || ((page) => page);

  const { store, props } = wrapper.useWrappedStore(pageProps);  // <---- Move this to Layout Component

  return getLayout(
    <Provider store={store}>                                    // <---- Move this to Layout Component
      <Component {...props.pageProps} />
    </Provider>                                                 // <---- Move this to Layout Component
  );
}

export default MyApp;
//withLayout.tsx

import AuthLayout from "../components/auth/AuthLayout";
import AuthReduxLayout from "../components/auth/AuthReduxLayout";
import DefaultLayout from "../components/auth/DefaultLayout";

type LayoutType = "default" | "auth" | "authRedux";

export default function withLayout(layoutType: LayoutType, title: string) {
  if (layoutType === "auth") {
    return function getLayout(page: React.ReactElement) {
      return <AuthLayout title={title}>{page}</AuthLayout>;
    };
  }

  if (layoutType === "authRedux") {
    return function getLayout(page: React.ReactElement) {
      return <AuthReduxLayout title={title}>{page}</AuthReduxLayout>;
    };
  }

  return function getLayout(page: React.ReactElement) {
    return <DefaultLayout title={title}>{page}</DefaultLayout>;
  };
}
//AuthReduxLayout.tsx

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { Fragment } from "react";
import Footer from "../../../src/components/layout/footer";
import Header from "../../../src/components/layout/header";

const queryClient = new QueryClient();

const AuthReduxLayout = (props: any) => {
  // const { store, props } = wrapper.useWrappedStore(pageProps);   //<--- Move to here

  return (
    <QueryClientProvider client={queryClient}>
      {/* <Provider store={store}>                                  //<--- Move to here     */}
      <Fragment>
        <Header />
        <main>{props.children}</main>
        <Footer />
      </Fragment>
      {/* </Provider>                                               //<--- Move to here     */}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
};

export default AuthReduxLayout;

I tried to access the parent pageProps using React.Children and then apply the next-redux-wrapper's useWrappedStore hook to access the required props, but it doesn't work.

//AuthReduxLayout.tsx (testing version)

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import React, { Fragment } from "react";
import { Provider } from "react-redux";
import Footer from "../../../src/components/layout/footer";
import Header from "../../../src/components/layout/header";
import { wrapper } from "../../redux/store/store";

const queryClient = new QueryClient();

const AuthReduxLayout = (parentProps: any) => {

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


  const updatedChildren = (pobjProps:any) => {
    
    return React.Children.map(parentProps.children,(child)=> React.cloneElement(child,{ ...pobjProps }))
  }

  const newChildren = updatedChildren(props);



  return (
    <QueryClientProvider client={queryClient}>
      <Provider store={store}>
      <Fragment>
        <Header />
        <main>{newChildren}</main>
        <Footer />
      </Fragment>
      </Provider>
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
};

export default AuthReduxLayout;

Any clues would be appreciated. Thanks


Solution

  • I figured it out. I had to pass the page.props in the withLayout hoc as a property of the component.

    if (layoutType === "authRedux") {
        return function getLayout(page: React.ReactElement) {
          return (
            <AuthReduxLayout title={title} pageProps={page.props}>
              {page}
            </AuthReduxLayout>
          );
        };
      }
    

    Then I am able to access that property in the child component like this:

    const AuthReduxLayout = (parentProps: any) => {
      const { store, props } = wrapper.useWrappedStore(parentProps.pageProps);
    
      return (
        <QueryClientProvider client={queryClient}>
          <Provider store={store}>
            <Fragment>
              <Header />
              <main>{props.children}</main>
              <Footer />
            </Fragment>
          </Provider>
          <ReactQueryDevtools initialIsOpen={false} />
        </QueryClientProvider>
      );
    };