skip to Main Content

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.

2

Answers


  1. 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]);
    
    Login or Signup to reply.
  2. 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]
      );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search