Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Hook useCallback not updating State value

I'm new to React Hooks. I have a UI with multiple inputs with values in an object. I create the UI with a loop, so I would like to have a single callback for the updating the inputs.

enter image description here

The "Shop" input uses it's own callback and successfully updates the state. But the "Product" inputs never update the state. I don't really understand why these two callbacks behave differently.

The code is below. The problem is in the handleChange callback.

import React, { useCallback, useState, useEffect } from "react";
import { TextField, Form } from "@shopify/polaris";

export default function TextFieldExample() {
  const [values, setValues] = useState({
    "prod-0": "Jaded Pixel",
    "prod-1": "blue diamonds"
  });
  const [shop, setShop] = useState("Joe's house of pancakes");

  const handleChangeShop = useCallback(newName => {
    setShop(newName);
  }, []);

  const handleChange = useCallback((newValue, id) => {
      console.log("Pre: values:", values);
      console.log(id, newValue);
      const newProds = values;
      newProds[id] = newValue;
      setValues(newProds);
      console.log("Post: newProds:", newProds);
    }, [values]);

  useEffect(() => {    // observing if State changes
    console.log("in useEffect: shop:", shop); // this gets called and is updated when changed.
    console.log("in useEffect: values:", values); // this never gets called or updated.
  }, [values, shop]);

  const items = [];
  Object.keys(values).forEach(function(prod) {
    items.push(
      <TextField label={"Product " + prod.substr(5)} id={prod} value={values[prod]} onChange={handleChange} />
    );
  });
  return (
    <Form>
      <TextField label="Shop" id="shop" value={shop} onChange={handleChangeShop}/>
      {items}
    </Form>
  );
}

Code Sandbox is here: https://codesandbox.io/s/fast-tdd-1ip38
Try it out and look at the console.

like image 689
furnaceX Avatar asked Mar 29 '20 07:03

furnaceX


People also ask

How do you update state in React hook?

To update the state, call the state updater function with the new state setState(newState) . Alternatively, if you need to update the state based on the previous state, supply a callback function setState(prevState => newState) .

Does useCallback cause Rerender?

The key takeaway here is that useCallback returns you a new version of your function only when its dependencies change, saving your child components from automatically re-rendering every time the parent renders.

How wait for state changes in React Hooks?

Use the useEffect hook to wait for state to update in React. You can add the state variables you want to track to the hook's dependencies array and the function you pass to useEffect will run every time the state variables change.

Does useState React Hooks update the state object directly?

One of React's most commonly used Hooks is useState , which manages states in React projects as well as objects' states. With an object, however, we can't update it directly or the component won't rerender.


2 Answers

You are mutating the values state, see this sandbox

Change your handleChange function to

const handleChange = useCallback((newValue, id) => {
  const newProds = { ...values };
  newProds[id] = newValue;
  setValues(newProds);
}, [values]);

You can change it further to

const handleChange = useCallback((newValue, id) => {
  setValues(prods => ({...prods, [id] : newValue }));
}, [values]);
like image 116
Sumanth Madishetty Avatar answered Sep 20 '22 13:09

Sumanth Madishetty


Try updating handleChange to this:

const handleChange = useCallback(
    (newValue, id) => {
      console.log("Pre: values:", values);
      console.log(id, newValue);
      setValues(state => ({ ...state, [id]: newValue}));
    },
    [values]
  );
like image 35
Danko Avatar answered Sep 22 '22 13:09

Danko