Search code examples
mongodbnext.jsreduxreact-reduxredux-toolkit

Can't extract data from Redux Toolkit 2.0 in my NextJS 14 app


I am creating a NextJs 14 ecommerce application using redux toolkit for state management. I have created a slice using my best understanding of RTK 2.0 syntax to call user data so that it can be added to my user state.

"use client";

import { buildCreateSlice, asyncThunkCreator } from "@reduxjs/toolkit";

const createSliceWithThunks = buildCreateSlice({
  creators: {
    asyncThunk: asyncThunkCreator,
  },
});

const initialState = {
  user: {
    _id: "",
    fname: "",
    lname: "",
    email: "",
    streetAddress: "",
    postalCode: "",
    city: "",
    county: "",
    country: "",
  },
  loading: false,
  error: null,
};

const userSlice = createSliceWithThunks({
  name: "user",
  initialState,
  reducers: (create) => ({
    fetchUser: create.asyncThunk(
      async (_, thunkApi) => {
        const response = await fetch("/api/user");
        if (!response.ok) {
          throw new Error("Failed to fetch user data");
        }
        const userData = await response.json();
        console.log(userData);
        return userData;
      },
      {
        pending: (state) => {
          (state.loading = true), (state.error = null);
        },
        fulfilled: (state, action) => {
          state.loading = false;
          state.data = action.payload;
        },
        rejected: (state, action) => {
          state.loading = false;
          state.error = action.error.message;
        },
      }
    ),
  }),
});

export const { fetchUser } = userSlice.actions;
export default userSlice.reducer;

This is my api/user route:

import { connectDb } from "@dbConfig/db";
import User from "@models/UserModel";
import { getServerSession } from "next-auth";
import { NextResponse } from "next/server";

export async function GET(req, res) {
  const session = getServerSession(); //using my session data from NextAuth to get email to find user in mongodb

  try {
    const email = (await session).user.email; //I don't know why prettier formatted this line like this, the code breaks if I change it.

    connectDb();

    const user = await User.findOne({ email });
    console.log(user);

    return NextResponse.json({ message: "User found:", user }, { status: 201 });
  } catch (error) {
    console.log(error);
  }
}

The call works perfectly, I am getting the user data from my database and can see it when I go to localhost:3000/api/user. The problem is when I try to get it using Redux-Toolkit and then set my user state to the data received. When I check the Redux-Devtools in my browser, I can see the data, but it is only appearing under a data property, and not actually updating state. I am at a loss for how to move forward. Any help is appreciated.

redux dev tool screenshot


Solution

  • The state is actually updating, you can see the updated state in the dev tool.

    However...

    There is no data property in the state until this fetchUser action is fulfilled, in which case the reducer case sets a state.data property equal to action.payload. I suspect you want to be setting the state.user property to the user property of the response JSON in the action payload, e.g. state.user = action.payload.user;.

    const initialState = {
      user: {
        _id: "",
        fname: "",
        lname: "",
        email: "",
        streetAddress: "",
        postalCode: "",
        city: "",
        county: "",
        country: "",
      },
      loading: false,
      error: null,
    };
    
    const userSlice = createSliceWithThunks({
      name: "user",
      initialState,
      reducers: (create) => ({
        fetchUser: create.asyncThunk(
          async (_, thunkApi) => {
            try {
              const response = await fetch("/api/user");
              const userData = await response.json();
              return userData;
            } catch(error) {
              return thunkApi.rejectWithValue(error);
            }
          },
          {
            pending: (state) => {
              state.loading = true;
              state.error = null;
            },
            fulfilled: (state, action) => {
              state.loading = false;
              state.user = action.payload.user; // <-- update user property
            },
            rejected: (state, action) => {
              state.loading = false;
              state.error = action.payload.message;
            },
          }
        ),
      }),
    });