Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Can't map fetched data from custom useFetch() hook

I created a custom useFetch() hook so I can make my code more dynamic and less repetitive. The problem is that I can’t display my data in App.js.

I get these errors:

Cannot read properties of undefined (reading ‘map’).

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

react-dom.development.js:67 Warning: Can’t perform a React state
update on an unmounted component. This is a no-op, but it indicates a
memory leak in your application. To fix, cancel all subscriptions and
asynchronous tasks in a useEffect cleanup function.

I did a console.log(genres) to see if there are any errors from my custom hook, but it works fine, logs all the genres. The problem is caused as soon as I try to display my data using the map method.

CodeSandbox link

useFetch.js

import { useReducer, useEffect } from "react";
import axios from "axios";

const ACTIONS = {
  API_REQUEST: "api-request",
  FETCH_DATA: "fetch-data",
  ERROR: "error",
};

const initialState = {
  data: [],
  loading: false,
  error: null,
};

function reducer(state, { type, payload }) {
  console.log(payload);
  switch (type) {
    case ACTIONS.API_REQUEST:
      return { ...state, data: [], loading: true };
    case ACTIONS.FETCH_DATA:
      return { ...state, data: payload, loading: false };
    case ACTIONS.ERROR:
      return { ...state, data: [], error: payload };
    default:
      return state;
  }
}

function useFetch(url) {
  const [state, dispatch] = useReducer(reducer, initialState);
  useEffect(() => {
    dispatch({ type: ACTIONS.API_REQUEST });
    axios
      .get(url)
      .then((res) => {
        dispatch({ type: ACTIONS.FETCH_DATA, payload: res.data });
      })
      .catch((e) => {
        dispatch({ type: ACTIONS.ERROR, payload: e.error });
      });
  }, [url]);
  return state;
}

export default useFetch;

App.js

import "./styles.css";
import useFetch from "./useFetch";

export default function App() {
  const BASE_URL =
    "https://api.themoviedb.org/3/genre/movie/list?api_key=${API_KEY}";
  const { data: genres, loading, error } = useFetch(BASE_URL);
  console.log(genres);
  return (
    <div className="App">
      {genres.genres.map((genre) => (
        <div key={genre.id}>{genre.name}</div>
      ))}
    </div>
  );
}

>Solution :

Your initial state has data as an array:

const initialState = {
  data: [],
  loading: false,
  error: null,
};

And your App component is trying to read the property genres on that array as soon as it loads. There is no property on an array with that name, so genres.genres is undefined, and the map call on it will throw an error.

I would initialise initialState.data as {genres: []}, by passing the data container as another argument to your hook rather than hardcoding it into the hook file.

function useFetch(url, data) {
  const [state, dispatch] = useReducer(reducer, {...initialState, data});
  ...
}
const { data: genres, loading, error } = useFetch(BASE_URL, {genres: []});
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading