如何基于数组创建多个useState并跟踪变化?或者只是将单个状态对象传递给多个组件?
在组件的底部,我尝试基于
customFilter
数组在循环中创建 options
。
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 */}
</>
);
}
工作 Stackblitz:https://stackblitz.com/edit/vite-react-state?file=src%2Fcomponents%2FCustomFormatBox.jsx
根据 @evolutionxbox 评论,带有
useReducer
的版本
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 function createInitialState(options) {
const emptyListsFromOptionsName = options.map((o) => [o.name, []])
const objWithLists = Object.fromEntries(emptyListsFromOptionsName);
return objWithLists;
}
function reducer(state, action) {
const result = {
...state
};
result[action.type] = action.right;
return result;
}
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: "2", label: "OPTICAL_BLANK_2" },
{ name: "8", label: "OPTICAL_BLANK_8" },
{ name: "10", label: "SAMPLE_BLANK_0" },
{ name: "11", label: "SAMPLE_BLANK_1" },
{ name: "20", label: "SYS_CUV_6" },
{ name: "21", label: "SYS_CUV_11" },
{ name: "22", label: "SYS_CUV_28" },
{ name: "253", label: "OPTICAL_DACS" }
]
},
{
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 [filtersAll, dispatch] = React.useReducer(reducer, createInitialState(options));
const customFilter = (option, right, setRight) => (
<Accordion key={option.name}>
<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 (
<div>
{options.map((option) => {
return customFilter(option, filtersAll[option.name], (right) => dispatch({ type: option.name, right: right }))
})}
</div>
)
}
一般来说不建议基于数组生成状态,这样最终会导致状态难以管理。在这种情况下,也许可以考虑使用单个状态对象。我根据您现有的代码做了一些更改,希望对您有所帮助!
https://stackblitz.com/edit/vite-react-state-vwd49u?file=src%2Fcomponents%2FCustomFormatBox.jsx