Search code examples
reactjsaxiosuse-effect

"TypeError: listOfTours.map is not a function" while trying to display fetched data in react


I'm struggling since two days on this and afters hours of trial and error I couldn't solve it.

What I'm trying to achieve is that I can display my fetched data, which I get from my backend. I saw several example on different websites, but I can't get it to work.

Here is my code and the error I get:

function Tours({ isAuth: isAuth, component: Component, ...rest }) {
  const [listOfTours, setListOfTours] = useState([]);
  console.log(listOfTours);

  const getAllTours = () =>
    axios
      .get("/api/all-tours", {
        headers: {
          "x-access-token": localStorage.getItem("token"),
        },
      })
      .then((response) => {
        if (response.data.status === "error") {
          console.log(response.data.status);
        } else {
          setListOfTours(response.data);
        }
      });

  useEffect(() => {
    getAllTours();
  }, []);

  if (listOfTours.length > 0) {
    return (
      <div>
        <div className="tourDisplay">
          {listOfTours.map((tour) => {
            return (
              <div>
                <div>Tourname: {tour.tour_name}</div>
                <div>...other values</div>
              </div>
            );
          })}
        </div>
      </div>
    );
  } else {
    return (
      <div>
        <p>Loading...</p>
      </div>
    );
  }
}

export default withRouter(Tours);

TypeError: listOfTours.map is not a function
Tours
src/components/tours.js:32
  29 | if (listOfTours.length > 0) {
  30 |   return (
  31 |     <div>
> 32 |       <div className="tourDisplay">
     | ^  33 |         {listOfTours.map((tour) => {
  34 |           return (
  35 |             <div>

I think it has something to do with the asynchronous call and that .map can't process with empty array. Therefor, I use the if statement in front of the return, but it also won't work.

Does someone have any idea, or am I missing something?


Solution

  • Try to make the getAllTours function as a Promise.

    ...
    const [listOfTours, setListOfTours] = useState(null);
    
    const getAllTours = async () => {
      try {
        // not sure if /api/all-tours is the proper API source
        const tours = await axios.get("/api/all-tours", {
            headers: {
              "x-access-token": localStorage.getItem("token"),
            },
        });
    
        // try to console.log here to make sure if the tours has responded successfully and what type of the tours.response.data has either object or array
        console.log(tours.response);
        console.log(typeof tours.response.data);
    
        // this seems to be tours.response without data
        if (tours.response.data) { // if (tours.response) {
          // if tours.response.data is a type string, then convert it to json object
          // setListOfTours(JSON.parse(tours.response.data));
          setListOfTours(tours.response.data);
          // setListOfTours(tours.response);
        }
      } catch (err) {
        console.error(err);
      }
    }
    
      useEffect(() => {
        getAllTours();
      }, [getAllTours]);
    
      return (
        <div>
          {
            !listOfTours
              ? // loading div
              : <div className="tourDisplay">
              {listOfTours.length > 0 && listOfTours.map((tour) => {
                return (
                  <div>
                    <div>Tourname: {tour.tour_name}</div>
                    <div>...other values</div>
                  </div>
                );
              })}
            </div>
          }
        </div>
      );
    

    If this doesn't work, please inspect the browser and go to the network tab, and then make sure the API worked properly.

    enter image description here