React.js 复用可配置组件渲染多个Kendo ComboBox过滤器时数据错乱
问题诱因
1. 隐式全局变量污染(核心原因)
原代码在给filterDefinition、filterData赋值时,没有添加const/let声明关键字,浏览器会自动把这两个变量挂载到全局window对象上,所有6个DropDownFilterable组件共享同一个全局变量实例,这也完全匹配观察到的异常现象:
- 同步渲染阶段:每个组件执行赋值操作后立刻渲染标题,读操作和赋值操作的时间差极短,所以每个过滤器的标题都能正确显示对应内容;
- 异步请求阶段:所有接口的响应回调属于宏任务,会等所有同步渲染逻辑全部执行完才会触发,此时全局的
filterDefinition已经被最后一个组件覆盖为自身的过滤器定义,所有请求拿到响应后都向同一个全局filterData写入值,最终最后一次写入的结果会被所有组件的ComboBox读取,就出现了“全部下拉框共用最后一个过滤器数据源”的问题。
2. 未使用React状态存储异步数据
即使修正了全局变量的问题,直接给普通变量赋值filterData = response也无法触发组件重渲染。React的界面更新完全依赖props、state的变化感知,普通JS变量的修改对React是无感知的,异步数据返回后ComboBox依然会保持初始空值,无法正常加载内容。
修正实现
按照以下逻辑调整代码,即可实现各下拉框加载对应配置数据源的预期效果:
- 所有组件内部用到的变量必须在组件作用域内完成声明,杜绝隐式全局变量
- 使用
useState为每个组件实例单独维护自己的下拉数据源,保证组件间状态完全隔离 - 接口响应返回后,调用setState方法更新当前组件的数据源,触发对应组件重渲染
- 为
useEffect补充正确的依赖项,保证每个组件仅在挂载时拉取一次对应ID的过滤器数据
修正后的核心组件代码参考:
const DropDownFilterable = (props) => { const appService = Service(); // 从props获取当前组件专属的过滤器定义,仅在当前组件作用域内生效 const filterDefinition = props.Definition; // 为每个组件单独初始化空的下拉数据源状态 const [filterData, setFilterData] = React.useState([]); React.useEffect(() => { // 按当前组件的过滤器ID拉取对应数据源 appService.getFilterValues(filterDefinition.Id).then(response => { // 更新当前组件的状态,不会影响其他同类型组件 setFilterData(response); }) // 依赖项设为当前过滤器ID,保证仅在ID变化时重新拉取数据 }, [filterDefinition.Id]); return ( <div> <div className="row" title={filterDefinition.DisplayName}>{filterDefinition.DisplayName}</div> <ComboBox id={`filterComboBox_${filterDefinition.Id}`} data={filterData} style={{zIndex: 999999}} dataItemKey={filterDefinition?.Definition?.DataHeaders[0]} textField={filterDefinition?.Definition?.DataHeaders[1]} /> </div> ) }
内容的提问来源于stack exchange,提问作者TPeel
相关产品推荐
相关产品推荐

