Reputation: 2113
I have this three Quantity inputs in a form. (Actually, it can be added more dynamically, approx 15 or more. But for the sake of simplification, I said three.)
| - | - | Quantity |
|---|---|----------|
| - | - | 3 |
| - | - | 4 |
| - | - | 5 |
I want to add another input above them to add a single value and set them all same.
| - | - | Quantity |
|---|---|----------|
| - | - | 3 | <--- This input will set the same value for inputs below
|---|---|----------|
| - | - | 3 |
| - | - | 3 |
| - | - | 3 |
But the original three Quantity inputs will be still able to change each value.
Here's my attempt:
// form.tsx
...
const [value, setValue] = useState(0 || undefined)
const handleValue = (e: any) => setValue(e.target.value)
...
{/* Unify input to set the same value in inputs below */}
<input value={value} onChange={handleValue} />
{/* Without onChange, input will be set readOnly automatically, so I tried adding something */}
<input value={value} onChange={() => undefined} />
<input value={value} onChange={(e: any) => e.target.value} />
<input value={value} onChange={null} />
<input value={value} onChange={false} />
...
Unify input works, it sets values. But I can't edit inputs below. What am I doing wrong?
Stack - React:v16.13.1, TypeScript:v3.9.5
Upvotes: 1
Views: 185
Reputation: 1482
Solution with customizable number of inputs:
const NUM_INPUTS = 3;
const range: string[] = (Array.from(Array(NUM_INPUTS).keys()).map(i => ''));
const [value, setValue] = useState<string | undefined>(undefined);
const [values, setValues] = useState(range);
// Handle unified value
const handleValue = (e: any) => {
setValue(e.target.value);
setValues(range.fill(e.target.value));
}
// Handle individual inputs
const handleOtherValue = (e: any) => {
values[+e.target.name] = e.target.value;
setValues(values);
}
// Create array of input elements
const inputs = range.map((_, i) =>
(<input key={i} name={i.toString()}
defaultValue={values[i]} onChange={handleOtherValue}/>));
// Important to use defaultValue so inputs are mutable
return (
<div>
<input onChange={handleValue}/>
<Fragment key={value}> {/* important for React to auto update these inputs */}
{inputs}
</Fragment>
</div>
);
Upvotes: 1
Reputation: 5529
You need to store unify value and rest values in 2 separate state
const [valueAll, setValueAll] = useState(0)
const [value, setValue] = useState({
input1: 0,
input2: 0,
input3: 0
})
const handleValue = (e: any) => {
setValueAll(e.target.value)
let newValue = value;
Object.keys(newValue).forEach(key => {
newValue[key] = e.target.value
});
setValue(newValue)
}
<input value={valueAll} onChange={handleValue} />
<input value={value.input1} name="input1" onChange={handleEachValue} />
<input value={value.input2} name="input2" onChange={handleEachValue} />
<input value={value.input3} name="input3" onChange={handleEachValue} />
You can check here codesandbox
Upvotes: 1
Reputation: 106
The issue seems to come from all of the inputs having the same value
(the value
state). This provides an easy way for you to update all values at once but as you can see doesn't allow updating them individually. The only way they can change is if setValue()
is called which is in the onChange
event in the unify value.
To correct this, you'd want each value to have individual state. This could be done by storing an array of values in state, but for simplicity I just implemented additional useState
hooks.
export const Form = () => {
const [unifyValue, setUnifyValue] = useState(0);
const [firstValue, setFirstValue] = useState(0);
const [secondValue, setSecondValue] = useState(0);
const [thirdValue, setThirdValue] = useState(0);
const handleUnifyValue = (e: any) => {
setUnifyValue(e.target.value);
setFirstValue(e.target.value);
setSecondValue(e.target.value);
setThirdValue(e.target.value);
}
const handleFirstValue = (e: any) => {
setFirstValue(e.target.value);
}
const handleSecondValue = (e: any) => {
setSecondValue(e.target.value);
}
const handleThirdValue = (e: any) => {
setThirdValue(e.target.value);
}
return (
<form>
{/* Unify input to set the same value in inputs below */}
<input value={unifyValue} onChange={handleUnifyValue} />
{/* Without onChange, input will be set readOnly automatically, so I tried adding something */}
<input value={firstValue} onChange={handleFirstValue} />
<input value={secondValue} onChange={handleSecondValue} />
<input value={thirdValue} onChange={handleThirdValue} />
</form>
)
}
If I can get the state array working nicely I'll update this with that example. That would allow more than just 3 inputs and also reduce the need for so many useState
and handleXValue
lines.
Upvotes: 1