Super new to React and trying out something. I have 3 toggle switches on a single screen. Should I be using a separate state handler for every switch?
Right now I have used just one, but obviously this will not work as expected because isChecked
is being shared by all the three toggle switches.
const [isChecked, setIsChecked] = useState(false)
const data = [
['Sample Text 1', <ToggleSwitch showLabel={true} checked={isChecked} onChange={() => setIsChecked(!isChecked)}/>],
['Sample Text 2', <ToggleSwitch showLabel={true} checked={isChecked} onChange={() => setIsChecked(!isChecked)}/>],
['Sample Text 3', <ToggleSwitch showLabel={true} checked={isChecked} onChange={() => setIsChecked(!isChecked)}/>]
]
Can I use some sort of id to change the state of individual switches using the same handler(isClicked
)? Or is it recommended to use separate state handlers for every switch?
CodePudding user response:
It really deepends what you want to achive.
The useState React hook controlls of one state. The useReducer can control multiple states which related.
CodePudding user response:
Yes, if it's a static form, usually you just use several useState
. Because you can make sure the variable(state) names are readable, and it's easy to add new fields (or remove unneeded ones). It might look dumb but the maintainability and readability are better than any kind of "clever code".
But sometimes we may have a more dynamic form. For example, all the fields are from some backend APIs, or you have a huge list/table and the toggle fields don't have any semantic meanings, then of course you have to write the code in a more dynamic way.
Here is an example that uses a useReducer
:
const initialState = {
1: { checked: false },
2: { checked: false },
3: { checked: false },
4: { checked: false },
}
function reducer(state, action) {
const { type, payload } = action
switch (type) {
case 'toggle':
return {
...state,
[payload.id]: { checked: !state[payload.id].checked },
}
}
}
const YourComponent = () => {
const [toggleState, dispatch] = useReducer(reducer, initialState)
return (
<div>
{Object.keys(toggleState).map((id) => (
<ToggleSwitch
key={id}
checked={toggleState[id].checked}
onChange={() => dispatch({ type: 'toggle', payload: { id } })}
/>
))}
</div>
)
}
I'm using objects here. An array of objects is also fine, or even a simple array: [false, false, false, false]
. I think it depends on your use case.
It's also possible to use just one setState
, but useReducer
in this case is flexible. You can easily add new actions like checkAll
or uncheckAll
, and put your logics in the reducer.
And then, you can even extract this to a custom hook. Please check this CodeSandbox https://codesandbox.io/s/stackoverflow-toggles-l9b2do?file=/src/App.js for a full working example.
And just for your reference, I also tried to modify your code and used just useState
and an array:
const YourComponent = () => {
const [toggles, setToggles] = useState([
['text 1', true],
['text 2', false],
['text 3', false],
['text 4', false],
])
const handleChange = (index) => {
const newState = [...toggles]
newState[index] = [newState[index][0], !newState[index][1]]
setToggles(newState)
}
return (
<div>
{toggles.map(([text, checked], index) => (
<ToggleSwitch
key={index}
checked={checked}
onChange={() => handleChange(index)}
/>
))}
</div>
)
}