javascript 嵌套列表功能组件的React重新呈现

anauzrmj  于 2023-03-16  发布在  Java
关注(0)|答案(1)|浏览(98)

自定义组件CheckboxLabels是否应在每次handleCheckboxChange调用后检索不同的checked属性?当前我无法在关闭封装的上层列表之前将单击的复选框设置为选中状态。只有打开和关闭上层列表才会更新嵌套复选框组件的状态。

type CheckboxLabelsProps = {
  columns: string[]
  table: string
  checked: Map<string, boolean>
  handleCheckboxChange: (event: React.ChangeEvent<HTMLInputElement>) => void
}

function CheckboxLabels({ columns, table, checked, handleCheckboxChange }: CheckboxLabelsProps) {
  return (
    <FormGroup>
      {columns.map((column, index) => (
        <FormControlLabel
          key={column}
          label={column}
          value={`${table}-${column}`}
          control={<Checkbox checked={checked.has(`${table}-${column}`) ? checked.get(`${table}-${column}`) : false} onChange={handleCheckboxChange} />}
        />
        ))}
    </FormGroup>
  );
}

export default function NestedList({ items }: PropsType) {
  const [open, setOpen] = React.useState([] as boolean[]);
  const [checked, setChecked] = React.useState(new Map() as Map<string, boolean>);

  useEffect(() => {
    // sets open state as array of false
  }, [items])

  useEffect(() => {
    // sets checked state for each nested checkbox element as false
  }, [])

  const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const [table, column] = event.target.value.split('-');
    const newChecked = checked;
    const key = `${table}-${column}`;
    newChecked.set(key, !checked.get(`${table}-${column}`));
    setChecked(newChecked);
  };

  const handleClick = (index: number) => {
    const collapsedIndex: number = index;
    const newOpen = [...open];
    newOpen[collapsedIndex] = !(open[collapsedIndex]);
    setOpen(newOpen);
  };

  return (
    <List>
      {items.map((item, index) => 
        {
          const tables = Object.keys(item.payload.columns);
          const columnsForTable = new Map<string, string[]>();
          tables.forEach((table) => {
              const key = table;
              const value = item.payload.columns[table];
              columnsForTable.set(key, value);
          });
          const columnsFT = Object.entries(Object.fromEntries(columnsForTable.entries()));
          return  columnsFT.map(([table, columns]: [string, string[]], index ) => 
                      (
                        <Box key={table}>
                          <ListItemButton onClick={() => handleClick(index)}>
                            <ListItemIcon>
                            <InboxIcon />
                            </ListItemIcon>
                            <ListItemText primary={table} />
                            {open[index] ? <ExpandLess /> : <ExpandMore />}
                          </ListItemButton>
                            <Collapse in={open[index]} timeout="auto" unmountOnExit>
                                <List key={table} component="div" disablePadding>
                                  <ListItem sx={{ pl: 4 }}>
                                    <Box sx={{ display: 'flex', flexDirection: 'column', ml: 3 }}>
                                      <CheckboxLabels columns={columns} table={table} checked={checked} handleCheckboxChange={handleCheckboxChange}/>
                                    </Box>
                                  </ListItem>
                                </List>
                            </Collapse>
                          </Box>
                      ))
          })
      }
    </List>
  );
}
vs91vp4v

vs91vp4v1#

问题在于您的handleCheckboxChange实现;您应该创建checked Map的新副本,而不是重用旧副本。这是因为,作为性能优化,React仅在新状态值与旧状态值不同时调用setState后重新呈现(我相信通过使用Object.is()作为相等的概念)但是,通过重用相同的Map示例,checked Map的引用值不会改变。这样“React”就永远不知道要放弃了。
以下handleCheckboxChange实现应该可以解决您的问题:

const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  const [table, column] = event.target.value.split('-');
  const newChecked = new Map(checked); // <-- create copy
  const key = `${table}-${column}`;
  newChecked.set(key, !checked.get(`${table}-${column}`));
  setChecked(newChecked);
};

相关问题