React.js+TypeScript+Material UI动态生成权限开关:遍历对象数组实现独立功能UI Switch
Got it, let's refactor your component to handle dynamic permissions instead of relying on hardcoded checkedA/checkedB states. This way, no matter how many permissions get passed in, each will have its own independent switch.
Here's the step-by-step fix:
1. Initialize State from Props
Instead of hardcoding state keys, we'll build our initial state object using the permission_name from each permission in your data_permissions prop. We'll also add a useEffect to update state if the permission list changes later.
2. Update the Change Handler
Use React's functional setState to safely update state based on the latest values—this avoids issues with stale state references.
3. Render One Switch Per Permission
Remove the duplicate hardcoded Switches, and render a single Switch that links directly to the current permission's permission_name (for both the name prop and the checked state lookup).
Full Refactored Code
import React, { useState, useEffect } from 'react'; import { Container, Card, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, FormGroup, FormControlLabel, Switch } from '@mui/material'; import { useStyles } from './your-styles-file'; // Adjust import as needed // Define your props type clearly interface Permission { name: string; description: string; permission_name: string; } interface PermissionListProps { data_permissions: Permission[]; } export const PermissionList: React.FC<PermissionListProps> = ({ data_permissions }) => { const classes = useStyles(); // Initialize state with dynamic permission keys, defaulting to false const [permissionStates, setPermissionStates] = useState<Record<string, boolean>>({}); // Update state when the permission list changes useEffect(() => { const initialStates = data_permissions.reduce((acc, permission) => { acc[permission.permission_name] = false; // Set initial checked state here (could also pass initial values if needed) return acc; }, {} as Record<string, boolean>); setPermissionStates(initialStates); }, [data_permissions]); const handleSwitchChange = (e: React.ChangeEvent<HTMLInputElement>) => { const { name, checked } = e.target; // Use functional update to get latest state setPermissionStates(prevState => ({ ...prevState, [name]: checked })); }; return ( <Container> <Card> <TableContainer className={classes.scroll} component={Paper}> <Table aria-label="permission table"> <TableHead> <TableRow> <TableCell className={classes.name} align="left">Permission Name</TableCell> <TableCell className={classes.description} align="left">Permission Description</TableCell> <TableCell align="left">Switch</TableCell> </TableRow> </TableHead> <TableBody> {data_permissions.map(permission => ( <TableRow key={permission.permission_name}> {/* Use permission_name as unique key */} <TableCell className={classes.name}>{permission.name}</TableCell> <TableCell className={classes.description}>{permission.description}</TableCell> <TableCell> <FormGroup row className={classes.Permission}> <FormControlLabel control={ <Switch checked={permissionStates[permission.permission_name] ?? false} // Fallback to false if state hasn't loaded yet onChange={handleSwitchChange} name={permission.permission_name} // Link switch to the permission's unique identifier /> } label="Off/On" /> </FormGroup> </TableCell> </TableRow> ))} </TableBody> </Table> </TableContainer> </Card> </Container> ) };
Key Notes:
- We use
Record<string, boolean>for the state type to enforce that keys are strings (permission names) and values are booleans. - The
useEffectensures that if thedata_permissionsprop updates (e.g., new permissions are added), our state will sync up automatically. - We use
permission.permission_nameas thekeyfor each TableRow—this is better thannamebecause it's a unique identifier (permission names might collide, butpermission_nameshould be unique). - The
?? falsein the Switch'scheckedprop is a safety fallback in case the state hasn't finished initializing yet.
内容的提问来源于stack exchange,提问作者Seb

