React: Uncaught TypeError: Cannot read properties of undefined (reading 'recipe_name')

Viewed 1117

Just wondering what is going wrong here (I am new to react). My fetch request works and brings in data from backend when the user makes a query in input. But now I receive this error.

RecipeDBTest.jsx:54 Uncaught TypeError: Cannot read properties of undefined (reading 'recipe_name')
    at GetRecipe (RecipeDBTest.jsx:54:1)
    at renderWithHooks (react-dom.development.js:16175:1)
    at mountIndeterminateComponent (react-dom.development.js:20913:1)
    at beginWork (react-dom.development.js:22416:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4161:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4210:1)
    at invokeGuardedCallback (react-dom.development.js:4274:1)
    at beginWork$1 (react-dom.development.js:27405:1)
    at performUnitOfWork (react-dom.development.js:26513:1)
    at workLoopSync (react-dom.development.js:26422:1)

This is the relevant React component's code:

import './RecipeForm.css'
import React, { useState } from 'react'
import PageContainer from '../components/ui/PageContainer.js'

export default function GetRecipe() {
  const [name, setName] = useState('')
  const [recipeInfo, setRecipeInfo] = useState('')

  const retrieveRecipe = function (e) {
    e.preventDefault()
    console.log(name)
    fetch(`http://localhost:3001/getjoinedrecipes/?name=${name}`, {
      method: 'GET',
      headers: { 'Content-type': 'application/json' },
    })
      .then((resp) => resp.json())
      .then((json) => {
        console.log(json)
        let result = json.map((row, idx) => {
          return <span key={idx}>{row}</span>
        })
        setRecipeInfo(result)
      })
  }

  return (
    <div>
      <div className="recipe-form-container">
        <form className="recipe-form">
          <div className="page-header">
            <h1>Get Recipe!</h1>
          </div>
          <div className="recipe-title recipe-element">
            <label>Recipe Name</label>
            <input
              type="text"
              value={name}
              onChange={(e) => setName(e.target.value)}></input>
          </div>

          <div className="get-button-container">
            <button className="get-recipe" onClick={(e) => retrieveRecipe(e)}>
              Get Recipe
            </button>
          </div>
        </form>
      </div>
      <PageContainer
        RecipeName={recipeInfo[0].recipe_name}
      />
    </div>
  )
}

Is this something to do with state? I don't fully understand how it is going wrong, as it works when I remove the <PageContainer> and RecipeName prop. Here is the returned data from the fetch call:

(3) [{…}, {…}, {…}]
0:
id: 11
ingredient_id: 11
ingredient_name: "lemon"
measure: "cups"
quantity: "2"
recipe_blurb: "Delicious"
recipe_id: 6
recipe_name: "Bolognese"
1 Answers

You are initializing recipeInfo as a string, updating it as an array of react objects and accessing them (when passing props to PageContainer) as API data response object.
You can't access recipe_name as recipeInfo[0].recipe_name.

Update your state initialization as:

  const [recipeInfo, setRecipeInfo] = useState([])

Then check for data before accessing it, as:

    <PageContainer
        recipeName={recipeInfo.length ? recipeInfo[0].recipe_name:""}
      />

Also you should store the array returned from API in recipeInfo state instead of storing elements. Move the mapping of data to return of PageContainer (This isn't needed if you are using only the first element of the array) .

What Should Go in State?

State should contain data that a component's event handlers may change to trigger a UI update. In real apps this data tends to be very small and JSON-serializable. When building a stateful component, think about the minimal possible representation of its state, and only store those properties in state.

Refer this for more details.

So update the retrieveRecipe function as

const retrieveRecipe = function (e) {
    e.preventDefault()
    console.log(name)
    fetch(`http://localhost:3001/getjoinedrecipes/?name=${name}`, 
    {
      method: 'GET',
      headers: { 'Content-type': 'application/json' },
    })
      .then((resp) => resp.json())
      .then((json) => {
        setRecipeInfo(json)
      })
  }
Related