Search code examples
javascriptreactjsreact-hooksjavascript-objects

Why this map function does not work, although code looks fine?


In my react functional component I am returning a list of notifications. When the useEffect() is called the "data" is generated and then shown on page. The code runs fine but the map() function does not show desired effect, no list is seen... I get results on console but the object called "data" I am generating just doesnt works properly with map() or may be some other issue is there which i can not see.. My code is :

import React from 'react'
import { useEffect } from 'react';
import { useState } from 'react'

export default function Notifications() {

  var data = [];
  var categoryNames = [];
  var budgetsArray = [];
  var datesArray = [];

  const sessionInfo = JSON.parse(window.localStorage.getItem("session"));

  useEffect(
    () => {
      handleShowNotification();
    }
  )

  async function getAllNotifications() {
    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");
    myHeaders.append("Cookie", "connect.sid=s%3A80dace41-48b4-47d8-a444-f8febddbfd90.1vgh6QFP%2FtUfsEdt%2B%2F91KgBJjKFVnMm6vghCiOGLmP8");

    var raw = JSON.stringify({
      "session_id": sessionInfo._id,
    });

    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: raw,
      redirect: 'follow'
    };

    var response = await fetch("http://localhost:8080/myNotifications", requestOptions);
    var result = await response.json()
    console.log("all notifications are " + result.data.notifications);
    return result.data.notifications;
  }

  async function getCategoryName(category_id) {
    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");
    myHeaders.append("Cookie", "connect.sid=s%3Adb8f2f2e-3680-4dd7-8b7d-cb022010cc7a.4AKLofa96yxCRAhjQJdkKKyaeOJpc0g%2FqQxJ9klQpmk");

    var raw = JSON.stringify({
      "session_id": sessionInfo._id,
      "category_id": category_id
    });

    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: raw,
      redirect: 'follow'
    };

    var response = await fetch("http://localhost:8080/fetchCategory", requestOptions);
    var result = await response.json();
    return result.data.category.category_name;
  }

  async function getBudget(categoryId) {
    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");
    myHeaders.append("Cookie", "connect.sid=s%3A80dace41-48b4-47d8-a444-f8febddbfd90.1vgh6QFP%2FtUfsEdt%2B%2F91KgBJjKFVnMm6vghCiOGLmP8");

    var raw = JSON.stringify({
      "session_id": sessionInfo._id,
      "category_id": categoryId
    });

    var requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: raw,
      redirect: 'follow'
    };

    var response = await fetch("http://localhost:8080/getBudget", requestOptions)
    var result = await response.json();
    return result.data.budget.amount;
  }



 async function handleShowNotification() {
    var notifications = await getAllNotifications();
    var count = 0;
    for (var notification of notifications) {
      count++;
      var categoryName = await getCategoryName(notification.category_id);
      console.log("category name "+categoryName);
      categoryNames.push(categoryName);
      var budgetAmount = await getBudget(notification.category_id);
      console.log("budget amount "+budgetAmount);
      budgetsArray.push(budgetAmount);
      var date = notification.date_of_creation;
      console.log("date "+date);
      datesArray.push(date);
    }
    for (var i=0; i<count; i++) {
        data.push({
          categoryName:categoryNames[i],
          budgetAmount:budgetsArray[i],
          date:datesArray[i],
        });
    }
    console.log("length of data is "+data.length)
  }

  function getFullData(item) {
    return (<li>The catgeory name is {item.categoryName}and budget is{item.budgetAmount}</li>);
  }

  return (
    <div>
      <br />
      <br />
      <div className="row">
        {/*<button onClick={handleShowNotification} style={{ width: "50%" }}>Show notifications</button>*/}
        <ul>
         //Problem is here, this is not shown
        {data.map(getFullData)}
        </ul>
      </div>
    </div>
  )
}

The code-sandbox link reflecting the problem is here.... https://codesandbox.io/s/immutable-morning-359c2r?file=/src/App.js


Solution

  • Issue

    data and the other variables are redeclared each render cycle as local variables. They are also mutated each render cycle. The useEffect hook is also missing a dependency array and updates the state which leads to render looping.

    Solution

    Convert data and any of the other variables to a React state variable using the useState hook. When state is updated this will trigger React to rerender the component with the updated state value.

    const [data, setData] = useState([]);
    

    Add a dependency array to the useEffect hook so it's not triggering a state update each and every render cycle.

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

    Each mapped element needs to have a proper React key. Since the data array elements haven't any unique id properties I suggest adding on and populating that property when adding new items to the data state.

    Example:

    import { nanoid } from 'nanoid';
    
    ...
    
    function handleShowNotification() {
      setData((data) =>
        data.concat({
          id: nanoid(),
          categoryName: "food",
          budgetAmount: 1000
        })
      );
    }
    
    ...
    
    function getFullData(item) {
      return (
        <li key={item.id}>
          The catgeory name is {item.categoryName}and budget is{item.budgetAmount}
        </li>
      );
    }
    
    ...
    
    <ul>{data.map(getFullData)}</ul>
    

    Edit why-this-map-function-does-not-work-although-code-looks-fine