React JS表格多开关Bug修复:解决单点击触发全选问题
问题修复方案
问题根源
你当前的代码中,所有同类型的开关(比如所有"Lihat"开关)都绑定了同一个全局状态变量和切换函数,导致点击任意一个开关时,全局状态同步改变,所有同类型开关跟着切换。
修复步骤
1. 重构状态结构
将分散的单个状态合并为一个对象,专门存储每个菜单(含子菜单)对应权限的独立状态。用useState管理这个对象,通过菜单ID、子菜单标识和权限类型组合成唯一键,确保每个开关对应独立的状态项。
2. 实现通用切换函数
创建一个通用切换函数,接收菜单ID、子菜单标识、权限类型作为参数,精准修改对应状态项,避免全局状态污染。
3. 更新开关绑定逻辑
渲染表格时,为每个开关绑定对应的独立状态值和切换函数,实现单个开关的独立控制。
完整修复代码
import React, { useEffect, useState } from "react"; import { Table, Form } from "react-bootstrap"; export default function RoleForm() { // 初始化权限状态:每个菜单/子菜单的权限默认未勾选 const [permissionStates, setPermissionStates] = useState({}); // 通用切换函数:精准定位对应权限状态并切换 const togglePermission = (rowId, menuKey, permType) => { const stateKey = `${rowId}-${menuKey}-${permType}`; setPermissionStates(prev => ({ ...prev, [stateKey]: !prev[stateKey] })); }; useEffect(() => { console.log("权限状态:", permissionStates); }, [permissionStates]); const tableHeader = [ "No", "Menu", "Sub Menu", "Lihat", "Tambah", "Ubah", "Hapus", "Export", ]; const tableData = [ { id: 1, menu: "Dashboard", submenu: ["-"], perms: ["lihat", null, null, null, "export"], }, { id: 2, menu: "Konfigurasi/Pengaturan", submenu: ["Akun Pengguna", "Grup", "Antrian", "Rawat"], perms: ["lihat", "tambah", "edit", "hapus", null], }, ]; return ( <Table className="table-responsive"> <thead> <tr> {tableHeader.map((header, index) => ( <th key={index} className="txt-blue-grey-700 base-md text-bold text-center" style={{ padding: "0.75rem" }} > {header} </th> ))} </tr> </thead> <tbody> {tableData.map((row, rowIndex) => ( <React.Fragment key={row.id}> {/* 主菜单行 */} <tr> <td>{rowIndex + 1}</td> <td>{row.menu}</td> <td>{row.submenu[0]}</td> {/* Lihat开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-main-lihat`] || false} onChange={() => togglePermission(row.id, "main", "lihat")} /> </td> {/* Tambah开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-main-tambah`] || false} onChange={() => togglePermission(row.id, "main", "tambah")} /> </td> {/* Ubah开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-main-ubah`] || false} onChange={() => togglePermission(row.id, "main", "ubah")} /> </td> {/* Hapus开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-main-hapus`] || false} onChange={() => togglePermission(row.id, "main", "hapus")} /> </td> {/* Export开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-main-export`] || false} onChange={() => togglePermission(row.id, "main", "export")} /> </td> </tr> {/* 子菜单行 */} {row.submenu.slice(1).map((subMenu, subMenuIndex) => ( <tr key={`${row.id}-sub-${subMenuIndex}`}> <td></td> <td></td> <td>{subMenu}</td> {/* 子菜单Lihat开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-sub-${subMenuIndex}-lihat`] || false} onChange={() => togglePermission(row.id, `sub-${subMenuIndex}`, "lihat")} /> </td> {/* 子菜单Tambah开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-sub-${subMenuIndex}-tambah`] || false} onChange={() => togglePermission(row.id, `sub-${subMenuIndex}`, "tambah")} /> </td> {/* 子菜单Ubah开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-sub-${subMenuIndex}-ubah`] || false} onChange={() => togglePermission(row.id, `sub-${subMenuIndex}`, "ubah")} /> </td> {/* 子菜单Hapus开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-sub-${subMenuIndex}-hapus`] || false} onChange={() => togglePermission(row.id, `sub-${subMenuIndex}`, "hapus")} /> </td> {/* 子菜单Export开关 */} <td> <Form.Check type="switch" checked={permissionStates[`${row.id}-sub-${subMenuIndex}-export`] || false} onChange={() => togglePermission(row.id, `sub-${subMenuIndex}`, "export")} /> </td> </tr> ))} </React.Fragment> ))} </tbody> </Table> ); }
关键说明
- 通过
permissionStates对象存储每个开关的独立状态,键采用${菜单ID}-${菜单类型(main/sub)}-${权限类型}的格式,确保唯一性。 togglePermission函数通过组合键精准定位要修改的状态项,避免全局状态影响其他开关。- 每个开关绑定对应的状态值(默认
false)和切换函数,实现单个开关的独立控制,同时修正了原代码中表头与内容列不匹配的布局问题。
内容的提问来源于stack exchange,提问作者github.arief
相关产品推荐
相关产品推荐

