React中useState更新滞后致C3图表与选中基因不同步问题
解决C3基因图表与选中基因名不同步的问题
问题根源
- useState异步更新特性:你在
updateGene中调用setGeneName后立刻用geneName查找数据,但React的useState更新是异步的,此时geneName还是更新前的旧值,导致gene状态更新滞后一步。 - C3图表重复初始化:组件每次渲染都会执行
C3.generate,既浪费资源,还会引发图表渲染异常。 - 冗余状态维护:
gene状态完全可以通过geneName从GENEDATA中推导出来,单独维护反而增加了同步风险。
修复后的代码
import React, { useState, useEffect, useRef } from "react"; import { Form } from "react-bootstrap"; import geneList from '../data/SampleSheet.json'; import C3 from 'c3'; import 'c3/c3.css'; const GENEDATA = [["Gnai3",501, 747, 705, 543, 689], ["genen",3,3,3,3],["Cdc45",22, 30 ,10 ,28, 29]] as [string, ...number[]][]; const GENENAMES = ["Gnai3", "genen", "Cdc45"]; export function Genes(): JSX.Element { // 仅维护基因名称状态,基因数据通过名称推导 const [geneName, setGeneName] = useState<string>(GENENAMES[0]); // 存储C3图表实例,避免重复创建 const chartRef = useRef<C3.Chart | null>(null); // 根据当前选中的基因名,推导对应的数据 const currentGene = GENEDATA.find(item => item[0] === geneName) || GENEDATA[0]; function updateGene(event: React.ChangeEvent<HTMLSelectElement>) { const newGeneName = event.target.value; setGeneName(newGeneName); // 直接用新选中的名称检查是否存在,不依赖异步更新后的状态 const geneExists = GENEDATA.some(item => item[0] === newGeneName); if (!geneExists) { alert("Gene not found!"); } } // 处理图表的初始化与更新 useEffect(() => { if (!chartRef.current) { // 首次渲染时初始化图表 chartRef.current = C3.generate({ bindto: '#chart', data: { columns: [currentGene] }, axis: { y: { label:"Activation Level" } }, color: { pattern: ['#ff7f0e'] }, }); } else { // 基因数据变化时,更新图表内容 chartRef.current.load({ columns: [currentGene], unload: true // 卸载旧数据,避免图表叠加 }); } // 组件卸载时销毁图表,清理资源 return () => { if (chartRef.current) { chartRef.current.destroy(); chartRef.current = null; } }; }, [currentGene]); // 依赖基因数据变化触发更新 return ( <div> <Form.Group controlId="Genes"> <Form.Label>Pick gene to display: </Form.Label> <Form.Select value={geneName} onChange={updateGene}> { GENENAMES.map((name) => <option key={name} value={name}>{name}</option> )} </Form.Select> <div id="chart"></div> </Form.Group> Selected Gene: {geneName} </div> ); }
关键修改说明
- 移除冗余状态:删除独立的
gene状态,通过geneName直接从数据源推导当前基因数据,从根源避免状态不同步问题。 - 用Ref管理图表实例:通过
chartRef保存C3图表对象,避免每次组件渲染都重新创建图表。 - useEffect控制图表生命周期:在
useEffect中完成图表初始化,当基因数据变化时调用load方法更新图表,而非重新生成。 - 直接使用新值处理逻辑:在
updateGene中直接使用event.target.value(新选中的基因名)进行校验,不再依赖异步更新后的geneName状态。
内容的提问来源于stack exchange,提问作者Andrew Long
相关产品推荐
相关产品推荐

