I have a rather simple NumberInput
component/control which handles its internal state (its current value) with useState
. The "outside world" is not interested in this state but only receives updates via the callback function onSubmit
which is called on enter & blur.
I also have a component called MinMaxNumberInputs
which is built of 2 NumberInput
s. Those 2 NumberInput
s don't know anything from each other and the parent MinMaxNumberInputs
has to deal with some "value syncing" by e.g. ensuring that min <= max etc.
This last part is where I'm a little stuck, as I don't know how to "force set" or override the internal state of the NumberInput
s from MinMaxNumberInputs
when needed.
Probably best explained via code:
NumberInput.tsx
// ...
type NumberInputProps = {
value: number;
onSubmit: (value: number) => void;
};
export default function NumberInput(props: NumberInputProps) {
// [1] props.value is obviously only applied on creation.
// Future changes have no effect.
const [value, setValue] = useState(props.value);
// Called on enter & blur
const submitValue = () => props.onSubmit(value);
// ...
return <input value={value} {/* ... */} />;
}
MinMaxNumberInputs.tsx
// ...
export const MinMaxValueInput = () => {
// Only for simpler showcasing, using global redux state in real world...
const [minValue, setMinValue] = useState(0);
const [maxValue, setMaxValue] = useState(0);
const onMinValueChanged = (newMinValue: number) => {
// [2] Ensure that entered value (min here) stays the same but adjust max if required
// ??? How to set adjusted value in max NumberInput ???
const newMaxValue = Math.max(newMinValue, maxValue);
setMinValue(newMinValue);
setMaxValue(newMaxValue);
};
const onMaxValueChanged = (newMaxValue: number) => {
// [3] Ensure that entered value (max here) stays the same but adjust min if required
// ??? How to set adjusted value in min NumberInput ???
const newMinValue = Math.min(minValue, newMaxValue);
setMinValue(newMinValue);
setMaxValue(newMaxValue);
};
// ...
return (
<>
<NumberInput value={minValue} onSubmit={onMinValueChanged} />
<NumberInput value={maxValue} onSubmit={onMaxValueChanged} />
</>
);
};
The thing is:
When newMaxValue
is adjusted in [2] (or newMinValue
in [3]), MinMaxNumberInputs
is holding the correct adjusted values in its state but those values are not propagated to the according NumberInput
and the shown values are therefore not updated/corrected.
I fully understand why this is not working. The value
property is only applied once to the NumberInput
s internal state at [1] but not anymore afterwards. However, I have no idea on how to overcome this without "breaking the boundaries" of the NumberInput
(e.g. using some global state there as well, ...).
I'd really appreciate some guidance on this!
Thanks
CodePudding user response:
Your NumberInput
is UNCONTROLLED component, your interface is misleading, it should be named initialValue
instead of value
.
Therefore what you want is whats called a "hybrid component", in this case when you DO pass value
prop, it becomes CONTROLLED:
export default function NumberInput(props: NumberInputProps) {
const [value, setValue] = useState(props.initialValue);
useEffect(() => {
setValue(props.value)
}, [props.value])
return <input value={value} />;
}