Home > Blockchain >  Assignments to the '[x]' variable from inside React Hook useEffect will be lost after each
Assignments to the '[x]' variable from inside React Hook useEffect will be lost after each

Time:06-19

I am new to react - just trying to figure out a problem I have with setting state with fetched data and useEffect() and then passing it through to a component.

What appears to happen is that an empty object is initially passed through to my prop, then the fetch call happens and another object (with the correct data) gets passed through, but the form in the child component doesn't update with the data.

I believe this has something to do with the useEffect() hook as the linter is telling me:

src/routes/EditRecipeFormWrapper.jsx
  Line 42:29:  Assignments to the 'fetchedRecipeData' variable from inside React Hook useEffect will be 
lost after each render. To preserve the value over time, store it in a useRef Hook and keep the 
mutable value in the '.current' property. Otherwise, you can move this variable directly inside 
useEffect  react-hooks/exhaustive-deps

I am trying to use state (setting state in the useEffect() hook to overcome this issue but unfortunately it is not working at this stage. I'd be grateful for any guidance.

Here is the code:

export default function EditRecipeFormWrapper() {
  let { id } = useParams()

  function handleUpdateRecipe(recipe) {
    console.log(recipe)
  }

  let fetchedRecipeData = {
    name: '',
    method: '',
    ingredients: [],
  }

  const [recipeData, setRecipeData] = useState({
    name: '',
    methodStepList: [],
    ingredientList: [],
    ingredientObject: {
      ingredient_name: '',
      quantity: '',
      measure: '',
    },
  })

  useEffect(() => {
    fetch(`/api/recipes/${id}`)
      .then((res) => res.json())
      .then((data) => {
        let recipeName = data[0].recipe_name
        let recipeMethod = data[0].recipe_method
        fetchedRecipeData = {
          name: recipeName,
          method: recipeMethod,
          ingredients: data,
        }
        setRecipeData(fetchedRecipeData)
      })
      .catch((err) => console.log(err))
  }, [id])

  console.log(recipeData)

  return (
    <div>
      <RecipeForm onSubmit={handleUpdateRecipe} data={recipeData} />
    </div>
  )
}

To give you an idea of what is happening in dev tools console:

{name: '', methodStepList: Array(0), ingredientList: Array(0), ingredientObject: {…}}
ingredientList: []
ingredientObject: {ingredient_name: '', quantity: '', measure: ''}
methodStepList: []
name: ""
[[Prototype]]: Object
RecipeForm.jsx:9 

{data: {…}, onSubmit: ƒ}
data: {name: '', methodStepList: Array(0), ingredientList: Array(0), ingredientObject: {…}}
onSubmit: ƒ handleUpdateRecipe(recipe)
[[Prototype]]: Object
EditRecipeFormWrapper.jsx:52 

//then the data is sent through
{name: 'Pasta Alla Gricia', method: 'Cook the guanciale [...]', ingredients: Array(4)}
ingredients: (4) [{…}, {…}, {…}, {…}]
method: "Cook the guanciale [...]
name: "Pasta Alla Gricia"
[[Prototype]]: Object
RecipeForm.jsx:9 

{data: {…}, onSubmit: ƒ}
data: {name: 'Pasta Alla Gricia', method: 'Cook the guanciale [...]', ingredients: Array(4)}
onSubmit: ƒ handleUpdateRecipe(recipe)
[[Prototype]]: Object

CodePudding user response:

You're declaring fetchedRecipeData inside of the component body.

React works on a system of render cycles. Whenever the state of the component is changed, your function is run and the output is used to update the DOM. But fetchedRecipeData is declared as a local variable in your function. That means every time your function is run, the variable is recreated and then thrown away after the return (just like any other local variable would be in a non-React JS program).

With this warning, React is trying to tell you that you're assigning a value to a local variable and that local variable will be thrown away at the end of the current render cycle. Usually, you can avoid this be either: (1) keeping the value you want in state (using a useState hook), or (2) keeping the value in a ref (using a useRef hook) which is similar to state but will not trigger a rerender when you change the value.

Looking at your code, it looks like the fetchedRecipeData isn't even necessary in the first place. You can just assign the results of your fetch directly to your state variable and skip the assignment to fetchedRecipeData.

export default function EditRecipeFormWrapper() {
  let { id } = useParams()

  function handleUpdateRecipe(recipe) {
    console.log(recipe)
  }

  const [recipeData, setRecipeData] = useState({
    name: '',
    method: '',
    ingredients: []
  })

  useEffect(() => {
    fetch(`/api/recipes/${id}`)
      .then((res) => res.json())
      .then((data) => {
        let recipeName = data[0].recipe_name
        let recipeMethod = data[0].recipe_method
        setRecipeData({
          name: recipeName,
          method: recipeMethod,
          ingredients: data,
        })
      })
      .catch((err) => console.log(err))
  }, [id])

  return (
    <div>
      <RecipeForm onSubmit={handleUpdateRecipe} data={recipeData} />
    </div>
  )
}

(I noticed that you seem to be switching between two different object shapes in the initial value of recipeData and fetchedRecipeData. I don't know the schema that your API returns, so I just chose one but you may need to tweak depending on the actual results.)

  • Related