如何在选中表格中的每个复选框时启用按钮

问题描述 投票:0回答:1

当选中表中的所有复选框时,我需要启用一个按钮,否则禁用。

带按钮的外部组件:

import Box from "@mui/system/Box";
import Stack from "@mui/system/Stack";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import { useState } from "react";
import HRMButton from "../Button/HRMButton";
import ToDoTable from "./ToDoTable";
import { fonts } from "../../Styles";

export default function OnboardingTasks({style}) {
    const tasks = [
        {
            name: 'Discuss and set your first 30/60/90-day goals with your manager',
            done: false
        },
        {
            name: 'Complete personal info & documents on Bluewave HRM',
            done: false
        },
        {
            name: 'Sign and submit essential documents',
            done: false
        }
    ];

    const [allTasksComplete, setAllTasksComplete] = useState(false);

    function checkTasks(tasks) {
        return tasks.every((task) => task.done);
    }

    return (
        <Box sx={{...{
            border: "1px solid #EBEBEB",
            borderRadius: "10px",
            minWidth: "1003px",
            paddingX: "113px",
            paddingY: "44px",
            fontFamily: fonts.fontFamily
        }, ...style}}>
            <h4 style={{textAlign: "center", marginTop: 0}}>Complete your to-do items</h4>
            <p style={{textAlign: "center", marginBottom: "50px"}}>
                You may discuss your to-dos with your manager
            </p>
            <ToDoTable 
                tasks={tasks} 
                onChange={() => setAllTasksComplete(checkTasks(tasks))} 
                style={{marginBottom: "50px"}}
            />
            <Stack direction="row" alignContent="center" justifyContent="space-between">
                <HRMButton mode="secondaryB" startIcon={<ArrowBackIcon />}>Previous</HRMButton>
                //This button needs to be enabled
                <HRMButton mode="primary" enabled={allTasksComplete}>Save and next</HRMButton>
            </Stack>
        </Box>
    );
};

表格组件:

import TableContainer from "@mui/material/TableContainer";
import Table from "@mui/material/Table";
import TableHead from "@mui/material/TableHead";
import TableBody from "@mui/material/TableBody";
import TableRow from "@mui/material/TableRow";
import TableCell from "@mui/material/TableCell";
import { styled } from "@mui/system";
import PropTypes from "prop-types";
import Checkbox from "../Checkbox/Checkbox";
import { fonts, colors } from "../../Styles";

export default function ToDoTable({tasks, onChange, style}) {
    //Custom style elements
    const TableHeaderCell = styled(TableCell)({
        color: colors.darkGrey,
        paddingTop: "10px",
        paddingBottom: "10px"
    });

    function handleChange(e, index, value) {
        console.log(tasks);
        tasks[index].done = value;
        onChange();
    }

    return (
        <TableContainer sx={{...{
            minWidth: "1003px",
            fontFamily: fonts.fontFamily
        }, ...style}}>
            <Table>
                <TableHead>
                    <TableRow sx={{backgroundColor: "#F9FAFB"}}>
                        <TableHeaderCell>
                            <b style={{color: colors.grey}}>To-Do</b>
                        </TableHeaderCell>
                        <TableHeaderCell align="right">
                            <b style={{color: colors.grey}}>Done</b>
                        </TableHeaderCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                    {tasks.map((task, index) => (
                        <TableRow>
                            <TableCell>
                                {task.name}
                            </TableCell>
                            <TableCell align="right">
                                <Checkbox
                                    type="checkbox"
                                    id={`${task.name}-done`}
                                    name={`${task.name}-done`}
                                    value={`${task.name}-done`}
                                    size="large"
                                    onChange={(e) => handleChange(e, index, !task.done)}
                                />
                            </TableCell>
                        </TableRow>
                    ))}
                </TableBody>
            </Table>
        </TableContainer>
    );
};

当使用 React

useState
时,如给出的代码示例中所示,每次
OnboardingTasks
状态更改时,整个
AllTasksComplete
组件都会重新渲染,这会将所有任务的状态重置回 false。当我使用 React
useRef
时,按钮不会重新渲染,并且不会对
AllTasksComplete
中的更改做出反应。

如何在选中所有复选框时启用该按钮,同时保留

tasks
变量的状态?

此示例中的按钮和复选框是 MUI 的自定义变体。如果您需要在本地计算机上运行此代码,请发表评论,我将提供代码。

javascript material-ui
1个回答
0
投票

您缺少一些 React 基础知识。主要问题是您使用的

tasks
数据被声明为 inside
OnboardingTasks
,因此任何时候
OnboardingTasks
出于任何原因渲染,
tasks
都会使用
done: false
属性重新声明。
tasks
应该是 React 状态,而
allTasksComplete
应该 not 处于状态,因为它是从
tasks
“真相来源”状态派生的值。

建议重构:

  • tasks
    转换为本地状态
  • allTasksComplete
    转换为记忆的派生值
  • 将更改处理程序移至
    OnboardingTasks
    并向下传递给
    ToDoTable
    作为 props
const initialState = [
  {
    name: 'Discuss and set your first 30/60/90-day goals with your manager',
    done: false
  },
  {
    name: 'Complete personal info & documents on Bluewave HRM',
    done: false
  },
  {
    name: 'Sign and submit essential documents',
    done: false
  }
];

export default function OnboardingTasks({ style }) {
  const [tasks, setTasks] = useState(initialState);

  const allTasksComplete = useMemo(() => tasks.every((task) => task.done));

  const handleChange = (index, done) => {
    // Use functional state update to shallow copy tasks state, 
    // and then shallow copy the task that is being updated
    setTasks(tasks => tasks.map((task, i) =>
      i === index
        ? { ...task, done }
        : task
    ));
  }

  return (
    <Box
      sx={{
        ...style
        border: "1px solid #EBEBEB",
        borderRadius: "10px",
        minWidth: "1003px",
        paddingX: "113px",
        paddingY: "44px",
        fontFamily: fonts.fontFamily
      }}
    >
      <h4 style={{textAlign: "center", marginTop: 0}}>
        Complete your to-do items
      </h4>
      <p style={{textAlign: "center", marginBottom: "50px"}}>
        You may discuss your to-dos with your manager
      </p>
      <ToDoTable 
        tasks={tasks} 
        onChange={handleChange} 
        style={{marginBottom: "50px"}}
      />
      <Stack direction="row" alignContent="center" justifyContent="space-between">
        <HRMButton
          mode="secondaryB"
          startIcon={<ArrowBackIcon />}
        >
          Previous
        </HRMButton>
        //This button needs to be enabled
        <HRMButton
          mode="primary"
          enabled={allTasksComplete}
        >
          Save and next
        </HRMButton>
      </Stack>
    </Box>
  );
};
  • TableHeaderCell
    声明移到 ReactTree 之外,这样它就是一个稳定的引用,并且不会在每个渲染周期重新声明
// Custom style elements
const TableHeaderCell = styled(TableCell)({
  color: colors.darkGrey,
  paddingTop: "10px",
  paddingBottom: "10px"
});

export default function ToDoTable({ tasks, onChange, style }) {
  return (
    <TableContainer
      sx={{
        ...style,
        minWidth: "1003px",
        fontFamily: fonts.fontFamily
      }}
    >
      <Table>
        <TableHead>
          <TableRow sx={{backgroundColor: "#F9FAFB"}}>
            <TableHeaderCell>
              <b style={{color: colors.grey}}>To-Do</b>
            </TableHeaderCell>
            <TableHeaderCell align="right">
              <b style={{color: colors.grey}}>Done</b>
            </TableHeaderCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {tasks.map((task, index) => (
            <TableRow key={index}>
              <TableCell>{task.name}</TableCell>
              <TableCell align="right">
                <Checkbox
                  type="checkbox"
                  id={`${task.name}-done`}
                  name={`${task.name}-done`}
                  value={`${task.name}-done`}
                  size="large"
                  onChange={() => onChange(index, !task.done)}
                />
              </TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.