How to use Redux Toolkit with Next.js and App-Router?
Asked Answered
B

3

5

How to use Redux toolkit in NextJS 13 App router ?

These are all my slices and code related to redux toolkit Is there anything wrong with the code coz I get this error in terminal saying , I think In Nextjs 13 we have to create a seperate provider like I did but I don't know what the error please help me find the error:

apiSlice.js:


import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

const baseQuery = fetchBaseQuery({
  baseUrl: "http://localhost:8000/",
  credentials: "include",
});

export const apiSlice = createApi({
  baseQuery,
  tagTypes: ["User"],
  endpoints: (builder) => ({}),
});

usersApiSlice.js:

import { apiSlice } from "./apiSlice";
const USERS_URL = "http://localhost:8000/api/users";

export const usersApiSlice = apiSlice.injectEndpoints({
  endpoints: (builder) => ({
    login: builder.mutation({
      query: (data) => ({
        url: `${USERS_URL}/auth`,
        method: "POST",
        body: data,
      }),
    }),
    register: builder.mutation({
      query: (data) => ({
        url: `${USERS_URL}`,
        method: "POST",
        body: data,
      }),
    }),
    logout: builder.mutation({
      query: () => ({
        url: `${USERS_URL}/logout`,
        method: "POST",
      }),
    }),
  }),
});

export const { useLoginMutation, useLogoutMutation, useRegisterMutation } =
  usersApiSlice;

authSlice.js :

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  userInfo: null,
};

const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    setCredentials: (state, action) => {
      state.userInfo = action.payload;
      localStorage.setItem("userInfo", JSON.stringify(action.payload));
    },
    logout: (state, action) => {
      state.userInfo = null;
      localStorage.removeItem("userInfo");
    },
  },
});

export default authSlice.reducer;
export const { setCredentials, logout } = authSlice.actions;

provider.js:

"use client";

import { Provider } from "react-redux";
import { store } from "./store";

export function Providers({ children }) {
  return <Provider store={store}>{children}</Provider>;
}

export default Providers;

store.js :

import { configureStore } from "@reduxjs/toolkit";
import authReducer from "./features/auth/authSlice";
import { apiSlice } from "./features/api/apiSlice";

export const store = configureStore({
  reducer: {
    auth: authReducer,
    [apiSlice.reducerPath]: apiSlice.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(apiSlice.middleware),
  devTools: true,
});

layout.js:

"use client";
import "./globals.css";
import { Providers } from "./GlobalRedux/provider";
// import { ToastContainer } from "react-toastify";
// import "react-toastify/dist/ReactToastify.css";

import { Inter } from "next/font/google";
import Header from "./components/Header";
import { Provider } from "react-redux";
import { store } from "./GlobalRedux/store";

const inter = Inter({ subsets: ["latin"] });

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Provider store={store}>
          <Header />
          {children}
        </Provider>
      </body>
    </html>
  );
}

login/page.jsx :

"use client";
import { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useLoginMutation } from "@/app/GlobalRedux/features/api/usersApiSlice";
import { setCredentials } from "@/app/GlobalRedux/features/auth/authSlice";
import { useRouter } from "next/navigation";
// import { toast } from "react-toastify";
import styles from "../loginregister.module.css";

const page = () => {
  const router = useRouter();

  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const dispatch = useDispatch();

  const [login, { isLoading, error }] = useLoginMutation();

  const { userInfo } = useSelector((state) => state.auth);

  useEffect(() => {
    if (userInfo) {
      router.push("/");
    }
  }, [router, userInfo]);

  const submitHandler = async (e) => {
    e.preventDefault();
    try {
      const res = await login({ email, password });
      dispatch(setCredentials({ ...res }));
      if (res.data.status === 201) {
        router.push("/");
      }
    } catch (err) {
      alert(err?.data?.message || err.error);
      // toast.error(err?.data?.message || err.error);
    }
  };

  return (
    <div className={styles.form}>
      <h1>Login</h1>

      <form onSubmit={submitHandler}>
        <input
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          type="email"
          name="email"
          placeholder="Email"
        />
        <input
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          type="password"
          name="password"
          placeholder="Password"
        />
        {isLoading && <h2>Loading...</h2>}
        <button>Login</button>
      </form>
    </div>
  );
};

export default page;
Biceps answered 21/5, 2023 at 16:41 Comment(3)
What is the error?Haematin
- error Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider> at page (login/page.jsx:29:78) nullBiceps
I have the same problem, I'm using NextJS 13, any update in this?Counteroffensive
C
5

Create à ReduxProvider in jsx or tsx :

"use client";
import {Provider} from "react-redux"
import {store} from "./store";
import React from "react";

export default function ReduxProvider({children}: { children: React.ReactNode }) {
    return <Provider store={store}>{children}</Provider>;
};

And in your Layout :

import './globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import ReduxProvider from "@/app/store/ReduxProvider";

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
      <ReduxProvider>
        {children}
      </ReduxProvider>
      </body>
    </html>
  )
}

Cockcroft answered 16/7, 2023 at 13:19 Comment(1)
Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?Affirm
K
1

I solved this problem by adding 'use client' to the component in which I was making a data request via RTK Query. Since Redux is initialized only on the client, server components (components without the 'use client' header) cannot use RTK.

Kame answered 21/7, 2023 at 14:44 Comment(0)
H
0

You have created your Providers file and component, but are not using it.

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Providers>
          <Header />
          {children}
        </Providers>
      </body>
    </html>
  );
}
Haematin answered 21/5, 2023 at 17:35 Comment(2)
sorry I pasted the wrong code I have used Providers but it still shows the same errorBiceps
This is the error : - error Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider> at page (login/page.jsx:29:78) nullBiceps

© 2022 - 2024 — McMap. All rights reserved.