Home > Mobile >  React, why state doesn't change?
React, why state doesn't change?

Time:09-21

It's a simple example about derived state.

Here is the sandbox url: https://codesandbox.io/s/review-react-derived-state-2-forked-v9ucpr?file=/src/App.js

I expected value change 0 to 10, when I click the button. But, it doesn't rendering also, its flow is weired. I click button several times, it doesn't re-render, even though changing the value.

Here is the code:

import { useState, useEffect } from "react";

export default function App() {
  console.log("App is called!");
  const [value, setValue] = useState(() => {
    console.log("App useState is alloc");
    return 0;
  });
  console.log("App is END");
  return (
    <div>
      <NumberInput value={value} onChange={setValue} />
      <button onClick={() => setValue(10)}>change to ten</button>
    </div>
  );
}

function NumberInput({ value, onChange }) {
  console.log("   NumberInput is called!");
  const [inputValue, setInputValue] = useState(value);

  useEffect(() => {
    const numberValue = Number(inputValue);
    onChange(numberValue);
  }, [inputValue, onChange]);
  console.log("   NumberInput is END");
  return (
    <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
  );
}

CodePudding user response:

Your

const [inputValue, setInputValue] = useState(value);

"forks" the prop value to local state, so changes to value won't be reflected in inputValue after initial mount. You'd need

useEffect(() => setInputValue(value), [value]);

in the child component to mirror any changes to the value prop to inputValue too.

CodePudding user response:

You are not updating the internal state of the NumberInput when the value changes.

This would be the code that solves the issue:

import { useState, useEffect } from "react";

export default function App() {
  const [value, setValue] = useState(0);

  return (
    <div>
      <NumberInput value={value} onChange={setValue} />
      <button onClick={() => setValue(10)}>change to ten</button>
    </div>
  );
}

function NumberInput({ value, onChange }) {
  const [inputValue, setInputValue] = useState(value);

  useEffect(() => {
    onChange( inputValue);
  }, [inputValue, onChange]);

  // We add a use effect that will get trigger when the parent
  // value changes
  // However, this implies that the NumberInput will always render
  // because of the previous useEffect, which also updates the 
  // value's value
  useEffect(() => {
    setInputValue(`${value}`);
  }, [value]);

  return (
    <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
  );
}

CodePudding user response:

useState will catch your value only on initialising the child component. Try:

<input value={value}

// instead of 

<input value={inputValue}

I hope that is what you wanted!

  • Related