React useState更新后自定义节点属性未同步渲染问题
问题根因
这是典型的闭包旧值问题:default_config是组件首次加载时定义的常量,其中viewGenerator渲染函数在初始化时就捕获了当时的circleR初始值("50")。后续setCircleR触发组件重渲染时,你传给<Graph>的config引用没有发生变化,react-d3-graph不会重新执行viewGenerator生成新的节点内容,所以自定义节点里读取到的永远是初始化时捕获的旧状态值。
可落地方案
方案1:显式传参+更新config引用(改造成本低)
不要让CustomNode读取外层闭包的circleR,把半径值作为显式props传入节点,同时在circleR变化时更新config引用,触发Graph重绘节点:
- 修改CustomNode,从props取半径值:
function CustomNode(props) { return ( <div> <svg viewBox="-1 -1 2 2"> <circle cx="0" cy="0" r={props.circleR} strokeWidth="0.1" stroke="black" fill="orange" /> </svg> </div> ); }
- 用
useMemo生成动态config,依赖circleR确保状态更新时生成新的config引用,同时把最新的半径值传入节点:
const graphConfig = useMemo(() => ({ ...default_config.d3_config, node: { ...default_config.d3_config.node, viewGenerator: (node) => ( <CustomNode node={node} focusId={null} circleR={circleR} /> ) } }), [circleR]);
- 渲染Graph时传入动态生成的config,同时绑定缩放回调:
<Graph config={graphConfig} onZoomChange={zoomChange} />
方案2:节点内部维护状态(性能更优)
如果不想每次缩放都触发全图表重绘,可以直接把缩放监听逻辑下沉到CustomNode内部,绕开外层闭包问题:
import { useTransformContext } from 'react-zoom-pan-pinch'; function CustomNode(props) { const [circleR, setCircleR] = useState("50"); // 直接从zoom-pan-pinch的上下文取最新的缩放状态 const { transformState } = useTransformContext(); useEffect(() => { setCircleR(transformState.scale > 1.311 ? "10" : "50"); }, [transformState.scale]); return ( <div> <svg viewBox="-1 -1 2 2"> <circle cx="0" cy="0" r={circleR} strokeWidth="0.1" stroke="black" fill="orange" /> </svg> </div> ); }
这种方案不需要修改外层config逻辑,缩放时只会更新对应节点本身,不会触发整个图表重绘。
补充提示:你贴的配置里写了
minZoom: 1, maxZoom: 1,这会完全禁用缩放交互,记得根据业务需求调整缩放范围。
内容的提问来源于stack exchange,提问作者walee
相关产品推荐
相关产品推荐

