Home > database >  passing object with children to a useState return an empty array
passing object with children to a useState return an empty array

Time:11-21

I recentyl started to learn javascrtipt, react, and NodeJS and I encountered a problem that the internet can't seem to fix. My nodeJs server publishes data on my localhost:7000 in the format (this works):

{
 "carbs":18,
 "fats":0, 
 "kcal":184,
 "productName":"Jenever bessen-",
 "protein":0, 
 "sugars":18
}, 
etc.

I then fetch the data in my hooks folder in request.js:

  const API_URL = 'http://localhost:7000';

  // Load planets and return as JSON.
  async function httpGetNutrition() {
    const response = await fetch(`${API_URL}/nutrition`); //works 
    console.log('fetched data')
    return await response.json(); //works
  }

  export {
    httpGetNutrition,
  };

Here After, I use the fetched data and try to add it in 'getNutrition'. To code I added currently maps the objects of 'fetchedNutrition' and adds them to the useState 'nutrition'. The 'item' object prints correctly, however, when I 'setNutrition' and console.log(nutrition), an empty array is passed.

    import { useCallback, useEffect, useState } from "react";
    import { httpGetNutrition } from "./requests";

    function UseNutrition()
    {   
        const [nutrition, setNutrition] = useState([])
        const [loading, setLoading] = useState(true)
        
        const getNutrition = useCallback(async () => {
            setLoading(true)
            try 
            {
                const fetchedNutrition = await httpGetNutrition(); //works
                fetchedNutrition.map((item) => {
                    console.log(item) //prints correctly
                    setNutrition([...nutrition, item]); //adds items to nutrition array
                    console.log(nutrition) //returns empty array
                })
                
                if (nutrition.length > 0) //nutrition.length returns 0 -> empty array
                {
                    console.log('set nutrition succesfully')
                    console.log(nutrition.length)
                }
                else
                {
                    console.log('data is of length 0')
                }
            
            }
            catch (err)
            {
                setLoading(false)
                console.log(`error: ${err}`)
            }
        },[]);

        if (loading)
        {
            <div className="loading">
            <h1>loading...</h1>
            </div>
        }

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

        return nutrition
    }

    export default UseNutrition;

Other ways I tried:

  • setNutrition([...nutrition, ...item])
  • remove the .map and add 'fetchedNutrition' to 'setNutrition': setNutrition(fetchedNutrition). However, this yields the following error for both cases:
react-dom.development.js:14887 Uncaught Error: Objects are not valid as a React child (found: object with keys {carbs, fats, kcal, productName, protein, sugars}). If you meant to render a collection of children, use an array instead.
    at throwOnInvalidObjectType

I have looked at similar posts only, which suggested both methods, but unfortunately non have worked for me yet. Not sure what is the root of the issue here, maybe anyone could suggest something?

Thanks in advance

I tried decomposing the object with ... notation, {} notation, mapping, defining the children in the useState method and adding the values in a loop.

CodePudding user response:

you cannot render arrays or objects in react (the last return of your function). try to do something like this:

    import { useCallback, useEffect, useState } from "react";
    import { httpGetNutrition } from "./requests";

    function UseNutrition()
    {   
        const [nutritions, setNutritions] = useState([])
        const [loading, setLoading] = useState(true)
        
        const getNutrition = useCallback(async () => {
            setLoading(true)
            try 
            {
                const fetchedNutrition = await httpGetNutrition(); //works
                setNutritions(fetchedNutrition)
            }
            catch (err)
            {
                console.log(`error: ${err}`)
            } finally {
                setLoading(false)
            }
        },[]);

        if (loading)
        {
            <div className="loading">
                <h1>loading...</h1>
            </div>
        }

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

        return nutritions.map((nutrition) => <div key={nutrition.productName}>{nitrition.productName}</div>)
    }

    export default UseNutrition;

CodePudding user response:

The map function is usually used to render array elements. You can set the nutrition state like this:

setNutrition([...fetchedNutrition]) // without fetchedNutrition.map()

And if you want to log the changes in nutrition state, i suggest u do that in a useEffect hook, something like this:

useEffect(() => {
  console.log(nutrition)
  if (nutrition.length > 0) {
    console.log('set nutrition succesfully')
    console.log(nutrition.length)
  } else {
    console.log('data is of length 0')
  }
}, [nutrition])

The above useEffect runs every time the nutrition state changes.

  • Related