You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React实现表格全选复选框时启用按钮并保留任务状态

解决React中任务全选时启用按钮的状态管理问题

问题背景

需求:当表格中所有复选框被勾选时启用"Save and next"按钮,否则禁用。
现有代码存在的问题:

  • 直接将tasks定义为普通数组,组件重渲染时会被重置为初始状态(所有done值为false)
  • 使用useState管理allTasksComplete时,状态变更触发组件重渲染,导致tasks被重置
  • 用useRef存储tasks则无法触发按钮重渲染,按钮状态无法随任务完成情况更新

解决方案

核心思路是将tasks作为React状态进行管理,同时通过子父组件的回调通信更新任务状态,确保状态变更能正确触发重渲染且保留任务状态。

1. 修改OnboardingTasks组件

将tasks改为useState状态变量,新增任务状态更新方法,直接基于状态计算全选状态:

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

export default function OnboardingTasks({style}) {
    // 将tasks转为useState状态,保留初始值
    const [tasks, setTasks] = useState([
        {
            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
        }
    ]);

    // 用useMemo缓存全选状态计算结果,避免重复计算
    const allTasksComplete = useMemo(() => {
        return tasks.every(task => task.done);
    }, [tasks]);

    // 定义更新单个任务完成状态的方法,传递给子组件
    const updateTaskDone = (index, isDone) => {
        setTasks(prevTasks => {
            const newTasks = [...prevTasks];
            newTasks[index].done = isDone;
            return newTasks;
        });
    };

    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} 
                onTaskChange={updateTaskDone} 
                style={{marginBottom: "50px"}}
            />
            <Stack direction="row" alignContent="center" justifyContent="space-between">
                <HRMButton mode="secondaryB" startIcon={<ArrowBackIcon />}>Previous</HRMButton>
                {/* 直接用计算好的全选状态控制按钮启用 */}
                <HRMButton mode="primary" enabled={allTasksComplete}>Save and next</HRMButton>
            </Stack>
        </Box>
    );
};

2. 修改ToDoTable组件

移除直接修改tasks的逻辑,改为调用父组件传递的回调方法更新状态,同时绑定复选框的checked属性:

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, onTaskChange, style}) {
    const TableHeaderCell = styled(TableCell)({
        color: colors.darkGrey,
        paddingTop: "10px",
        paddingBottom: "10px"
    });

    function handleChange(e, index) {
        // 取当前任务的相反状态,通知父组件更新
        const newDoneState = !tasks[index].done;
        onTaskChange(index, newDoneState);
    }

    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 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"
                                    checked={task.done} // 绑定任务完成状态,确保复选框显示正确
                                    onChange={(e) => handleChange(e, index)}
                                />
                            </TableCell>
                        </TableRow>
                    ))}
                </TableBody>
            </Table>
        </TableContainer>
    );
};

关键说明

  • 状态持久化:将tasks放入useState后,组件重渲染时会保留最新的任务状态,不会被重置
  • 不可变更新:使用函数式更新创建新数组,符合React状态不可变的原则,避免直接修改原数组导致的状态异常
  • 性能优化:useMemo缓存全选状态计算结果,减少不必要的数组遍历
  • 单向数据流:子组件通过回调通知父组件更新状态,遵循React的单向数据流设计原则

内容的提问来源于stack exchange,提问作者Gabriel Chan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:13:19