如何基于现有代码实现Chakra-UI二级嵌套Checkbox联动功能
二级嵌套复选框联动实现方案(基于Chakra-UI)
需求说明
- 点击父复选框时,所有子项(含二级子项)被选中
- 点击单个二级子项时,对应一级父项切换为不确定状态
- 某一级所有子项选中时,该级父项自动变为选中状态
现有代码状态
已完成第一级复选框联动逻辑,需扩展实现二级子项的联动功能,现有代码如下:
import React from "react"; import { Checkbox, Stack, Box } from "@chakra-ui/react"; const data = { users: [ { id: "user-1", name: "User 1", child: [{ name: "Salary1" }, { name: "Department1" }] }, { id: "user-2", name: "User 2", child: [{ name: "Salary2" }, { name: "Department2" }] }, { id: "user-3", name: "User 3", child: [{ name: "Salary3" }, { name: "Department3" }] }, { id: "user-4", name: "User 4", child: [{ name: "Salary4" }, { name: "Department4" }] }, { id: "user-5", name: "User 5", child: [{ name: "Salary5" }, { name: "Department5" }] }, { id: "user-6", name: "User 6", child: [{ name: "Salary6" }, { name: "Department6" }] }, { id: "user-7", name: "User 7", child: [{ name: "Salary7" }, { name: "Department7" }] }, { id: "user-8", name: "User 8", child: [{ name: "Salary8" }, { name: "Department8" }] } ] }; export default function App() { const [checkedItems, setCheckedItems] = React.useState( data.users.map(() => false) ); const allChecked = checkedItems.every(Boolean); const isIndeterminate = checkedItems.some(Boolean) && !allChecked; return ( <Stack pt={3} pl={3}> <Checkbox isChecked={allChecked} isIndeterminate={isIndeterminate} onChange={(e) => setCheckedItems(data.users.map(() => e.target.checked)) } > Parent Checkbox </Checkbox> <Stack pl={6} mt={2} spacing={2}> {data.users.map((user, index) => ( <Box key={user.id}> <Checkbox key={user.id} isChecked={checkedItems[index]} onChange={(e) => setCheckedItems([ ...checkedItems.slice(0, index), e.target.checked, ...checkedItems.slice(index + 1) ]) } > {user.name} </Checkbox> <Stack pl={6} spacing={2}> {user.child.map((category) => ( <Checkbox key={category.name}>{category.name}</Checkbox> ))} </Stack> </Box> ))} </Stack> </Stack> ); }
解决方案:修改后的完整代码
import React from "react"; import { Checkbox, Stack, Box } from "@chakra-ui/react"; const data = { users: [ { id: "user-1", name: "User 1", child: [{ name: "Salary1" }, { name: "Department1" }] }, { id: "user-2", name: "User 2", child: [{ name: "Salary2" }, { name: "Department2" }] }, { id: "user-3", name: "User 3", child: [{ name: "Salary3" }, { name: "Department3" }] }, { id: "user-4", name: "User 4", child: [{ name: "Salary4" }, { name: "Department4" }] }, { id: "user-5", name: "User 5", child: [{ name: "Salary5" }, { name: "Department5" }] }, { id: "user-6", name: "User 6", child: [{ name: "Salary6" }, { name: "Department6" }] }, { id: "user-7", name: "User 7", child: [{ name: "Salary7" }, { name: "Department7" }] }, { id: "user-8", name: "User 8", child: [{ name: "Salary8" }, { name: "Department8" }] } ] }; export default function App() { // 调整状态结构:二维数组,对应每个用户下的子项选中状态 const [checkedItems, setCheckedItems] = React.useState( data.users.map(user => user.child.map(() => false)) ); // 计算顶层复选框状态:所有子项全选则顶层选中,部分选则不确定 const allTopChecked = checkedItems.every(userItems => userItems.every(Boolean)); const isTopIndeterminate = checkedItems.some(userItems => userItems.some(Boolean)) && !allTopChecked; return ( <Stack pt={3} pl={3}> <Checkbox isChecked={allTopChecked} isIndeterminate={isTopIndeterminate} onChange={(e) => { // 点击顶层复选框时,同步所有一级和二级子项状态 const newState = data.users.map(user => user.child.map(() => e.target.checked) ); setCheckedItems(newState); }} > Parent Checkbox </Checkbox> <Stack pl={6} mt={2} spacing={2}> {data.users.map((user, userIndex) => { // 计算当前用户复选框的状态 const userAllChecked = checkedItems[userIndex].every(Boolean); const userIsIndeterminate = checkedItems[userIndex].some(Boolean) && !userAllChecked; return ( <Box key={user.id}> <Checkbox isChecked={userAllChecked} isIndeterminate={userIsIndeterminate} onChange={(e) => { // 点击用户复选框时,同步其下所有子项状态 const newUserItems = user.child.map(() => e.target.checked); const newState = [ ...checkedItems.slice(0, userIndex), newUserItems, ...checkedItems.slice(userIndex + 1) ]; setCheckedItems(newState); }} > {user.name} </Checkbox> <Stack pl={6} spacing={2}> {user.child.map((category, childIndex) => ( <Checkbox key={category.name} isChecked={checkedItems[userIndex][childIndex]} onChange={(e) => { // 点击二级子项时,更新对应位置的状态 const newUserItems = [ ...checkedItems[userIndex].slice(0, childIndex), e.target.checked, ...checkedItems[userIndex].slice(childIndex + 1) ]; const newState = [ ...checkedItems.slice(0, userIndex), newUserItems, ...checkedItems.slice(userIndex + 1) ]; setCheckedItems(newState); }} > {category.name} </Checkbox> ))} </Stack> </Box> ); })} </Stack> </Stack> ); }
关键改动说明
- 状态结构优化:将原来的一维布尔数组改为二维数组,每个一级用户对应一个子数组,存储其下二级子项的选中状态,更贴合嵌套结构
- 一级复选框联动:点击一级用户复选框时,自动同步其下所有二级子项的选中状态,同时根据子项状态计算自身的
isChecked和isIndeterminate - 二级子项联动:点击二级子项时,更新对应位置的状态,并实时触发一级复选框的状态重新计算,实现部分选中时的不确定状态
- 顶层复选框逻辑升级:点击顶层复选框时,同时选中/取消所有一级和二级复选框,保持全层级联动
内容的提问来源于stack exchange,提问作者Claude Vernet Michel
相关产品推荐
相关产品推荐

