In my project there are a lot of inputs so I've created each one of its own useState
, is there any way to reduce the use of useState
part of the code
const [OldGpa, setOldGpa] = useState(0);
const [oldHours, setOldHours] = useState(0);
const [firstHour, setFirstHour] = useState(0);
const [secondHour, setSecondHour] = useState(0);
const [threadHour, setThreadHour] = useState(0);
const [fourthHour, setFourthHour] = useState(0);
const [fifthHour, setFifthHour] = useState(0);
const [sixthHour, setSixthHour] = useState(0);
const [seventhHour, setSeventhHour] = useState(0);
const [firstPoint, setFirstPoint] = useState(0)
const [secondPoint, setSecondPoint] = useState(0)
const [threadPoint, setThreadPoint] = useState(0)
const [fourthPoint, setFourthPoint] = useState(0)
const [fifthPoint, setFifthPoint] = useState(0)
const [sixthPoint, setSixthPoint] = useState(0)
const [seventhPoint, setSeventhPoint] = useState(0)
const [GPA, setGPA] = useState(0)
code path: src/componentes/Form.js you can find the code here: https://codesandbox.io/s/jovial-smoke-yz1mk5?file=/src/components/Form/Form.js
CodePudding user response:
If you don't want to use any library, you can make all of your inputs an object like so :
const [inputs, setInputs] = useState({
OldGpa: 0,
oldHours: 0,
firstHour: 0,
secondHour: 0,
thirdHour: 0,
fourthHour: 0,
fifthHour: 0,
sixthHour: 0,
seventhHour: 0,
firstPoint: 0,
secondPoint: 0,
thirdPoint: 0,
fourthPoint: 0,
fifthPoint: 0,
sixthPoint: 0,
seventhPoint: 0
});
Now your state in an object so you have to "bind" value with value={inputs.property}
Your inputs will need a name attribute in order to update the right property on this object (understand, name attribute will have to be the same as the object property). You'll need to "copy" the prevState like so:
setInputs(prevState => ({
...prevState,
[name] : value
}))
Where name and value come from event
E.G
const handleInputs = (event) => {
// destructuring the object
let {target } = event;
let { name, value } = target;
setInputs(prevState => ({
...prevState,
[name] : value
}))
}
Or you can destructure the event right into the function parameter like so:
const handleInputs = ({target: { value, name}}) => {
setInputs(prevState => ({
...prevState,
[name] : value
}))
}
CodePudding user response:
// Outside the component:
const initialHours = Array(7).fill(0);
const initialPoints = Array(7).fill(0);
// Inside the component:
const [OldGpa, setOldGpa] = useState(0);
const [oldHours, setOldHours] = useState(0);
const [hours, setHours] = useState(initialHours);
const [points, setPoints] = useState(initialPoints);
const [GPA, setGPA] = useState(0)
Then whenever you used secondHour
, for instance, you'd use hours[1]
instead.
Instead of setSecondHour(newValue)
, it would be:
setHours(hours => {
hours = hours.slice(); // Copy the array
hours[1] = newValue;
return hours;
});
Sometimes you see people do that with spread and multiple slice
calls, or with Object.assign
, but to me those are clunky and unnecessarily complex. If you want to avoid rewriting that logic every time (which is a reasonable thing to want), write a utility function:
function setArrayElement(array, index, value) {
array = array.slice();
array[index] = value;
return array;
}
and use that when setting the second value:
setHours(hours => setArrayElement(hours, 1, newValue));
If you want, you can combine things further into an object that you store in state, as shown in the documentation.
Note
Unlike the
setState
method found in class components,useState
does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:const [state, setState] = useState({}); setState(prevState => { // Object.assign would also work return {...prevState, ...updatedValues}; });
Another option is
useReducer
, which is more suited for managing state objects that contain multiple sub-values.
(I'm not sure I agree with the "more suited" part, but useReducer
is another option.)
CodePudding user response:
You can consider this as a reference :
function init(initialCount) {
return {count: initialCount};
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}> </button>
</>
);
}
Similarly , you can modify according to your use case:
So, the answer to your question is ... to use useReducer instead of useState .
CodePudding user response:
This way also can be used to reduce the usage of multiple useStates. But, when comparing Runtime performance this way cannot be recomended.
const [{ OldGpa, oldHours,firstHour,secondHour }, setSuggest] = useState({
OldGpa:0, oldHours:0,firstHour:0,secondHour:0,threadHour:0, fourthHour:0
});
Are many useStates better than useState(object)?
I suggest you to use useReducer useReducer vs useState useReducer
CodePudding user response:
You can use an object and put all the inputs inside it.
const [inputs, setInputs]=useState({
OldGpa: 0,
oldHours: 0,
firstHour: 0,
secondHour: 0,
threadHour: 0,
fourthHour: 0,
fifthHour: 0,
sixthHour: 0,
seventhHour: 0,
firstPoint: 0,
secondPoint: 0,
threadPoint: 0,
fourthPoint: 0,
fifthPoint: 0,
sixthPoint: 0,
seventhPoint: 0,
GPA: 0,
})
and then have a function handleChange to pass the input to the states:
const handleChange=(e)=>{
setInputs({...inputs, [e.target.name]: e.target.value})
}
and then call it from every input something like:
<input type="number" name='OldGpa' onChange={handleChange}
value={inputs.OldGpa} />
...
<input type="number" name='OldHours' onChange={handleChange}
value={inputs.OldHours} />