0

I want to use the map() method on the cloned object but it keeps saying "Cannot read property 'map' of undefined" but I can use it to output the servings number ({copyRecipe.servings}). And I dont understand why console.log(copyRecipe) was shown 5 times with the first 4 empty (image below). Any helps would be appreciated. Thanks in advance!

const Parent = () =>{

  //I fetch data from my custom hook
  const {data: recipe, error, isLoading} = useFetch(url)

  return(
    <Child recipe={recipe}/>
  )
}

const Child = ({recipe}) =>{
  //I cloned the recipe from parent component
   const [copyRecipe, setCopyRecipe] = useState({});
   let duplicate = { ...copyRecipe };
   
   useEffect(() => {
   setCopyRecipe({...recipe});
   }, [recipe]);
   
   
   //this is to increase the number of servings when clicking the Increment component
   const incrementHandle = () => {
    duplicate.servings = `${parseInt(duplicate.servings) + 1}`;
    setCopyRecipe(duplicate);
  };
  console.log(copyRecipe);
   return(
   //I CAN USE THE CLONED OBJECT HERE!
   {copyRecipe && <p>{copyRecipe.servings}</p>}
    <Increment onClick={incrementHandle}/>
    
     //I CAN'T USE THE CLONED OBJECT HERE!!!
   {copyRecipe && (
      <div>
        {copyRecipe.ingredients.map((item,index) => {
          //do something
        })}
      </div>
   )}
   )
   
}
This is the console.log(copyRecipe)
Simon
  • 47
  • 1
  • 9

1 Answers1

2

So the console.log(copyRecipe) is firing every time the useEffect is being triggered.

Using Array.prototype.map is returning undefined, because it is undefined. You're trying to deconstruct from an api call which is going to be asynchronous.

Everything else, the useEffect of the child, the deconstruction and declaration of the variables of the parent are happening synchronously, and on mount, they aren't defined because the fetch hasn't evaluated yet.

Ok so you are using useEffect in the custom hook...

Now I think the problem is you're still trying to render the components based on an empty object.

Empty objects evaluate to truthy

I would recommend Object.entries(copyRecipe).length > 0 instead of copyRecipe as your conditional for rendering in the child component's return statement.

sloont
  • 204
  • 2
  • 8
  • Inside the parent component, I actually use the fetched data to render some components so I thought the data is also available to be passed to its child component. Speaking of your suggestions, I actually used useEffect in the custom Hook. Here is the link to it: https://www.dropbox.com/s/kxxfv0y0chk9hav/useFetch.png?dl=0 – Simon Jun 18 '21 at 02:34
  • Is there a reason why the dependency array for the child useEffect has recipe and not copyRecipe – sloont Jun 18 '21 at 02:36
  • So I'm not sure if I have to change any parts of it. I'm pretty new to react and it would be great if you can show me exactly what I should change in the custom Hook. Thank you :) – Simon Jun 18 '21 at 02:36
  • Speaking of the dependency, I actually posted a question about cloning an object and got that answer. From my understanding, every time the recipe changes the setCopyRecipe will be called – Simon Jun 18 '21 at 02:41
  • I actually think the custom hook is fine. I'm looking back through the code now. – sloont Jun 18 '21 at 02:42
  • Are you only rendering one parent and one child? – sloont Jun 18 '21 at 02:43
  • 1
    @Simon can we see a full print out of the copy recipe object? Also i think just checking if the object copyRecipe is truthy is always going to give you true. Maybe check Object.entries(copyRecipe).length > 0 – sloont Jun 18 '21 at 02:46
  • There are actually 3 children, the one I've been asking about is the second one. The first and second child share the same api data. Here is the link to it: https://www.dropbox.com/s/8i7zjqzv6rh6t0x/children.png?dl=0 – Simon Jun 18 '21 at 02:49
  • I changed the code like this: https://www.dropbox.com/s/hqotekmuwh7wp1x/object.entries.png?dl=0 – Simon Jun 18 '21 at 02:55