react-dropdown-tree-select组件SetState失效及重渲染异常求助
React-dropdown-tree-select 状态变更引发组件异常重渲染及报错问题解决
问题场景
使用react-dropdown-tree-select实现树形下拉框,基础功能正常,但触发状态变更时(无论是调用父组件的handleTreeUnitListChange,还是组件内部调用setState),组件出现异常重渲染,点击下拉框直接报错。环境版本:React 18.2、Node 18.15.0、NPM 9.5.0。
相关代码
树形下拉组件代码
import React, { useState, useEffect } from 'react'; import DropdownTreeSelect from 'react-dropdown-tree-select'; import 'react-dropdown-tree-select/dist/styles.css'; function TreeDropDown(props) { const { tree_unit_data, handleTreeUnitListChange } = props; const [treeData, setTreeData] = useState(tree_unit_data); useEffect(() => { // Update the treeData when tree_unit_data prop changes setTreeData(tree_unit_data); }, [tree_unit_data]); const onChange = (currentNode, selectedNodes) => { // Handle the onChange event here handleTreeUnitListChange(selectedNodes) // 注释此代码后组件恢复正常 }; return ( <DropdownTreeSelect data={treeData} onChange={onChange} texts={{ placeholder: 'Select Node' }} /> ); } export default TreeDropDown;
测试用树形数据
const treeData = [ { label: 'Node 1', children: [ { label: 'Node 1.1' }, { label: 'Node 1.2', children: [ { label: 'Node 1.2.1' }, { label: 'Node 1.2.2' }, ], }, ], }, { label: 'Node 2', children: [ { label: 'Node 2.1' }, { label: 'Node 2.2' }, ], }, ];
问题原因分析
- 状态同步冲突:组件内部通过
useState+useEffect同步父组件的tree_unit_data,同时在onChange中触发父组件状态更新,可能导致父组件重新传递新的tree_unit_data,进而触发子组件useEffect更新treeData,形成重渲染循环。 - 数据源与选中状态耦合:
react-dropdown-tree-select内部会维护自身的选中状态,若外部修改传入的data(或data引用频繁变化),会导致组件内部状态与外部数据源不一致,引发渲染异常。 - React 18严格模式影响:开发环境下严格模式会双重执行生命周期方法(如
useEffect),放大了状态同步的冲突问题。
解决方案
1. 移除冗余的内部状态同步
直接使用父组件传递的tree_unit_data作为数据源,无需在组件内部维护treeData状态,减少状态同步环节:
function TreeDropDown(props) { const { tree_unit_data, handleTreeUnitListChange } = props; const onChange = (currentNode, selectedNodes) => { handleTreeUnitListChange(selectedNodes); }; return ( <DropdownTreeSelect data={tree_unit_data} onChange={onChange} texts={{ placeholder: 'Select Node' }} /> ); }
2. 分离数据源与选中状态
如果需要父组件控制选中状态,使用组件的selected prop传递选中节点,而非通过修改数据源来控制:
// 父组件中 import { useState, useMemo } from 'react'; import TreeDropDown from './TreeDropDown'; function ParentComponent() { const [selectedNodes, setSelectedNodes] = useState([]); // 使用useMemo保持树形数据引用稳定 const treeData = useMemo(() => [ // 你的树形数据 ], []); return ( <TreeDropDown tree_unit_data={treeData} selectedNodes={selectedNodes} handleTreeUnitListChange={setSelectedNodes} /> ); } // 子组件修改 function TreeDropDown(props) { const { tree_unit_data, selectedNodes, handleTreeUnitListChange } = props; const onChange = (currentNode, selectedNodes) => { handleTreeUnitListChange(selectedNodes); }; return ( <DropdownTreeSelect data={tree_unit_data} onChange={onChange} selected={selectedNodes} texts={{ placeholder: 'Select Node' }} /> ); }
3. 确保数据源引用稳定
父组件中避免每次渲染都重新创建树形数据数组,使用useMemo缓存数据,防止不必要的重渲染:
const treeData = useMemo(() => [ { /* 树形数据结构 */ }, ], []); // 空依赖数组确保只初始化一次
4. 临时排查:关闭React严格模式
若问题仅出现在开发环境,可暂时关闭index.js中的严格模式(<React.StrictMode>标签),确认是否是严格模式放大了状态冲突问题,再针对性修复。
总结
核心解决思路是解耦数据源与选中状态,保持数据源引用稳定,避免不必要的状态同步循环,让react-dropdown-tree-select专注于自身的状态管理,外部仅通过onChange接收选中结果、selected控制选中状态即可。
内容的提问来源于stack exchange,提问作者Aaditya R Krishnan
相关产品推荐
相关产品推荐

