How to create multiple useState based on a array and keep track of changes? Or just pass a single state object to multiple components?
At the bottom of the component I’m trying to create customFilter
in a loop based on the options
array.
import * as React from 'react';
import TransferList from './TransferList';
import Accordion from '@mui/material/Accordion';
import AccordionSummary from '@mui/material/AccordionSummary';
import AccordionDetails from '@mui/material/AccordionDetails';
import Typography from '@mui/material/Typography';
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
export default function CustomFormatBox() {
const options = [
{
name: 'sample_type_key',
label: 'Sample Type',
defaultOperator: 'in',
valueEditorType: 'multiselect',
values: [
{ name: '0', label: 'OPTICAL_BLANK_0' },
{ name: '1', label: 'OPTICAL_BLANK_1' },
],
},
{
name: 'cuvette_number',
label: 'Cuvette Number',
defaultOperator: 'in',
valueEditorType: 'multiselect',
values: [
{ name: '0', label: '0' },
{ name: '1', label: '1' },
{ name: '2', label: '2' },
],
},
{
name: 'wavelength_key',
label: 'Wavelength',
values: [
{ name: '0', label: '340nm' },
{ name: '8', label: '850nm' },
],
},
];
const [filters, setFilters] = React.useState([]);
const [filters2, setFilters2] = React.useState([]);
// How to merge multiple state into single object?
const [allFilters, setAllFilters] = React.useState({
sample_type_key: [],
cuvette_number: [],
});
const customFilter = (option, right, setRight) => (
<Accordion>
<AccordionSummary
expandIcon={<ArrowDownwardIcon />}
aria-controls={`panel-${option.label}-content`}
id={`panel-${option.label}-header`}
>
<Typography>{option.label}</Typography>
</AccordionSummary>
<AccordionDetails>
<TransferList option={option} name right={right} setRight={setRight} />
<table>
<tbody>
{right.map((item) => (
<tr key={item.name}>
<td>{item.label}</td>
</tr>
))}
</tbody>
</table>
</AccordionDetails>
</Accordion>
);
return (
<>
{/* Eventually would like to create these filters in a loop based on options array. */}
{customFilter(options[0], filters, setFilters)}
{customFilter(options[1], filters2, setFilters2)}
{/* How to tell setAllFilters to set only in cuvette_number? */}
{customFilter(options[1], allFilters['cuvette_number'], () => {
setAllFilters((state) => ({ ...state, cuvette_number: state }));
})}
{/* Do not know how to do the update state part */}
</>
);
}
Working Stackblitz: https://stackblitz.com/edit/vite-react-state?file=src%2Fcomponents%2FCustomFormatBox.jsx
3
Answers
Per @evolutionxbox comment, version with
useReducer
Generally speaking it’s not recommended to generate states based on an array, which would make states unmanageable in the end. Maybe consider using a single state object in this case. I made some changes based on your existing code, hope it’s helpful!
https://stackblitz.com/edit/vite-react-state-vwd49u?file=src%2Fcomponents%2FCustomFormatBox.jsx
You can use an
Object
that includes the options as theinitial-state
and thenmap
over the options and can show the list of options.You need to change the state updating logic in the
TransferList
Component. This will ensure the dynamic rendering of the component and the updating of the state correctly.