React递归复选框状态管理:实现父子选中联动与正确存储
To fix the issue where unchecking a parent checkbox doesn't deselect its children, we need to handle descendant removal directly in the selection logic instead of relying on useEffect. Here's a straightforward implementation:
Step 1: Add Helper Functions
First, create two utility functions to traverse the tree and retrieve necessary node data:
// Find a node in the tree by its value const findNodeByValue = (nodes, targetValue) => { for (const node of nodes) { if (node.value === targetValue) return node; if (node.children) { const childMatch = findNodeByValue(node.children, targetValue); if (childMatch) return childMatch; } } return null; }; // Get all descendant values of a given node recursively const getDescendantValues = (node) => { let descendants = []; if (node.children) { node.children.forEach(child => { descendants.push(child.value); descendants = descendants.concat(getDescendantValues(child)); }); } return descendants; };
Step 2: Update Selection Handler
Modify the handleChange function to automatically remove all descendants when a parent checkbox is unchecked. This ensures the state stays consistent in a single update:
const handleChange = (value) => { setSelected(prevSelected => { if (prevSelected.includes(value)) { // Unchecking: remove current value and all its descendants from state const node = findNodeByValue(data, value); const descendantValues = getDescendantValues(node); return prevSelected.filter(item => item !== value && !descendantValues.includes(item) ); } else { // Checking: add only the current value (adjust here if you want to select all children on parent check) return [...prevSelected, value]; } }); };
Step 3: Sync Checkbox State to Selection
In your recursive TagTree component, ensure each checkbox's checked state is tied directly to the selected array:
const TagTree = ({ nodes, selected, onChange }) => { return ( <ul className="list-unstyled"> {nodes.map(node => ( <li key={node.value}> <Form.Check type="checkbox" label={node.label} value={node.value} checked={selected.includes(node.value)} onChange={() => onChange(node.value)} /> {node.children && node.children.length > 0 && ( <TagTree nodes={node.children} selected={selected} onChange={onChange} /> )} </li> ))} </ul> ); };
Key Improvement
This approach eliminates the need for useEffect by handling the parent-child sync upfront in the selection logic. We use the original tree data to find descendants directly, avoiding issues with accessing child values after state changes. The state will always only contain the values of currently checked checkboxes, and unchecking a parent will immediately remove all its children from the selection.
内容的提问来源于stack exchange,提问作者S L

