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.
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.
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) .
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.
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.
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.
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]);
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]
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With