Home > Mobile >  Why is child component not being updated?
Why is child component not being updated?

Time:11-05

I am trying to display data to and update data in child component from parent component. When I press edit button to update data, input element gets value from object but value in child component remains unchanged. Why is child component not getting value from parent component?

Parent component

import "./styles.css";
import { NumberInput} from "./NumberInput";
import { useState } from "react";

class Item {
  id: number = 0
  name: string = ''
  price: number = 0;
}

export default function App() {

  let [item, setItem] = useState(new Item());

  let tempItems = [];
  let tempItem = new Item();
  tempItem.id = 1;
  tempItem.name = "fish";
  tempItem.price = 5.5;
  tempItems.push(tempItem);

  let [items, setItems] = useState(tempItems);

  console.log(items); 

  return (
    <div className="App"><input 
        onChange={e => {
          let temp = {...item}; 
          temp.name = e.currentTarget.value;
          setItem(temp);
        }}
        value={item.name}
      /> Name
      <NumberInput item={item} name="price" setter={setItem}/> Price
      <button onClick={e => {
        let temp = [...items, item];
        setItems(temp);
        setItem(new Item());
      }}>Add</button>

      {items.map(x => {
        return (<div key={x.id}>
          <span>{x.name} =&gt; {x.price}</span>
          <button onClick={e => setItem(x)}>Edit</button>
        </div>)
      })}
    </div>
  );
}

Child component

interface NumberInputProps extends InputHTMLAttributes<HTMLInputElement> {
  item: any;
  setter: React.Dispatch<React.SetStateAction<any>>;
}

export const NumberInput: FC<NumberInputProps> = ({
  item,
  setter,
  ...rest
}) => {
  let tempValue = item[rest.name as string].toString();

  const [value, setValue] = useState(tempValue);

  const assignValue = (e: React.ChangeEvent<HTMLInputElement>) => {
    let value = e.currentTarget.value;
    if (!isNaN(parseFloat(value))) {
      let temp = { ...item };
      temp[rest.name as string] = parseFloat(value);
      setter(temp);
    }
    setValue(value);
  };

  return <input value={value} onChange={assignValue} {...rest} />;
};

CodePudding user response:

in your child component

  // item pass in as props, if item changed, child will re-render this is fine
  let tempValue = item[rest.name as string].toString();

  // but you put item's value to this local state as default
  // when item's value changed, your child value does not change
  // because "tempValue" only used as default when component first rendered
  const [value, setValue] = useState(tempValue);

Suggestion:
You can use the pass in item's value directly in your child
Put the value into a child local state is not necessary, and it may cause bug
if you really need to change the value and put it into another local state, you can do like this: (please beware that my code in JS not TS)

  const [value, setValue] = useState(null);

  // when ever item change, do setValue to refresh the value
  useEffect(() => {
    const tempValue = item[rest.name as string].toString();
    setValue(tempvalue);
  } ,[item])
  • Related