Paulliano
Paulliano

Reputation: 444

RTK Query in Redux-Toolkit is returning data of undefined, when I console.log the data it appears in console

I was trying to display an array of data fetched from my custom server with RTK Query using Next.js (React framework). And this is my first time using RTK Query. Whenever I console.log the data, it appears in the browser console. But whenever I try to map the data to render it in the browser, it keeps throwing an error saying Cannot read properties of undefined (reading 'map').

I figured Next.js always throws an error if an initial state is undefined or null even if the state change. This link talked about solving the problem using useMemo hook https://redux.js.org/tutorials/essentials/part-7-rtk-query-basics But I didn't understand it well. Please kindly help me out with displaying the data.

Here is the BaseQuery function example I followed, it was derived from redux toolkit docmentation https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#axios-basequery

import axios from "axios";

const axiosBaseQuery =
  ({ baseUrl } = { baseUrl: "" }) =>
  async ({ url, method, data }) => {
    try {
      const result = await axios({ url: baseUrl + url, method, data });
      return { data: result.data };
    } catch (axiosError) {
      let err = axiosError;
      return {
        error: { status: err.response?.status, data: err.response?.data },
      };
    }
  };

export default axiosBaseQuery;

I make the GET request here

import { createApi } from "@reduxjs/toolkit/query/react";
import axiosBaseQuery from "./axiosBaseQuery";

export const getAllCarsApi = createApi({
  reducerPath: "getAllCarsApi",
  baseQuery: axiosBaseQuery({
    baseUrl: "http://localhost:5000/",
  }),
  endpoints(build) {
    return {
      getAllCars: build.query({
        query: () => ({ url: "all-cars", method: "get" }),
      }),
    };
  },
});

export const { useGetAllCarsQuery } = getAllCarsApi;

This is my redux store

import { configureStore } from "@reduxjs/toolkit";
import { getAllCarsApi } from "./getAllCarsApi";
import { setupListeners } from "@reduxjs/toolkit/dist/query";

const store = configureStore({
  reducer: { [getAllCarsApi.reducerPath]: getAllCarsApi.reducer },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(getAllCarsApi.middleware),
});
setupListeners(store.dispatch);

export default store;

I provide the store to the _app.js file.

import "../styles/globals.css";
import axios from "axios";
import { MyContextProvider } from "@/store/MyContext";
import { Provider } from "react-redux";
import store from "@/store/ReduxStore/index";

axios.defaults.withCredentials = true;

function MyApp({ Component, pageProps }) {
  return (
    <MyContextProvider>
      <Provider store={store}>
        <Component {...pageProps} />
      </Provider>
    </MyContextProvider>
  );
}

export default MyApp;

I get the data here in my frontend.

import { useGetAllCarsQuery } from "@/store/ReduxStore/getAllCarsApi";

const theTest = () => {
  const { data, isLoading, error } = useGetAllCarsQuery();

  return (
    <div>
      {data.map((theData, i) => (
        <h1 key={i}>{theData}</h1>
      ))}
      <h1>Hello</h1>
    </div>
  );
};

export default theTest;

Upvotes: 8

Views: 17925

Answers (1)

phry
phry

Reputation: 44086

This is a timing thing.

Your component will always render immediately and it will not defer rendering until data is there. That means it will also render before your data has been fetched. So while the data is still loading, data is undefined - and you try to map over that.

You could do things like just checking if data is there to deal with that:

const theTest = () => {
  const { data, isLoading, error } = useGetAllCarsQuery();

  return (
    <div>
      {data && data.map((theData, i) => (
        <h1 key={i}>{theData}</h1>
      ))}
      <h1>Hello</h1>
    </div>
  );
};

Upvotes: 9

Related Questions