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!