当选中表中的所有复选框时,我需要启用一个按钮,否则禁用。
带按钮的外部组件:
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 的自定义变体。如果您需要在本地计算机上运行此代码,请发表评论,我将提供代码。
您缺少一些 React 基础知识。主要问题是您使用的
tasks
数据被声明为 inside OnboardingTasks
,因此任何时候 OnboardingTasks
出于任何原因渲染,tasks
都会使用 done: false
属性重新声明。 tasks
应该是 React 状态,而 allTasksComplete
应该 not 处于状态,因为它是从 tasks
“真相来源”状态派生的值。
建议重构:
tasks
转换为本地状态allTasksComplete
转换为记忆的派生值OnboardingTasks
并向下传递给 ToDoTable
作为 propsconst 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>
);
};