传递index参数给ref时触发无限循环的原因排查
为什么传递index给ref回调会触发无限循环?
先看你的两段代码对比:
正常运行的代码:
const container = useCallback((node: any) => { console.log(node); }, []); return ( <> {reports.map((report, i) => ( <Resizer ref={container} key={i}> {report} </Resizer> ))} </> );
--> 此代码运行正常,但未传递index参数
触发无限循环的代码:
const container = useCallback((node: any, index: number) => { console.log(index, node); }, []); return ( <> {reports.map((report, i) => ( <Resizer ref={(node) => container(node, i)} key={i}> {report} </Resizer> ))} </> );
--> 触发无限循环!!
问题根源:内联ref函数引发的重渲染循环
这里的核心问题出在你传递给<Resizer>的ref是一个每次渲染都会重新创建的内联箭头函数,而非useCallback稳定化后的container函数本身:
- 当你用
(node) => container(node, i)作为ref时,组件每一次渲染(比如reports变化、父组件状态更新触发的渲染),map循环里的每一个<Resizer>都会生成一个全新的箭头函数实例。 - React在处理ref时会严格对比引用:如果前后两次渲染的ref不是同一个函数,React会先调用旧的ref函数传入
null(清理之前的节点引用),再调用新的ref函数传入当前DOM节点。 - 如果
<Resizer>组件内部对ref的变化做出了响应(比如在ref更新时触发状态变更、或执行了导致组件重渲染的逻辑),或者父组件因为<Resizer>的重渲染而再次触发自身渲染,就会形成渲染→创建新ref→Resizer重渲染→父组件重渲染→创建新ref的无限循环。
而第一段代码中,你直接传递的是useCallback包裹的container函数——这个函数的引用在组件生命周期内是稳定的(因为useCallback的依赖数组为空),所以React不会重复执行ref的清理和重新绑定,自然不会触发额外的重渲染循环。
可行解决方案(不使用全局变量)
方式1:为每个索引创建稳定的回调函数
用useMemo配合useCallback,为每个索引生成一个稳定的回调,避免每次渲染创建新函数:
const containerCallbacks = useMemo(() => { return reports.map((_, i) => useCallback((node: any) => { console.log(i, node); }, []) ); }, [reports]); return ( <> {reports.map((report, i) => ( <Resizer ref={containerCallbacks[i]} key={i}> {report} </Resizer> ))} </> );
这里useMemo会在reports变化时重新生成回调数组,每个回调都是useCallback稳定化后的实例,不会触发不必要的ref更新。
方式2:用ref对象存储索引与节点的映射
如果不需要在ref回调中立即处理逻辑,也可以用一个ref对象来统一管理所有节点和对应的索引:
const nodesRef = useRef<Map<number, any>>(new Map()); const container = useCallback((node: any, index: number) => { if (node) { nodesRef.current.set(index, node); } else { nodesRef.current.delete(index); } console.log(index, node); }, []); return ( <> {reports.map((report, i) => ( <Resizer ref={(node) => container(node, i)} key={i}> {report} </Resizer> ))} </> );
这个方式虽然仍用了内联函数,但如果<Resizer>不会因为ref变化触发重渲染,就不会产生循环。不过更稳妥的还是方式1。
内容的提问来源于stack exchange,提问作者Junhoflow
相关产品推荐
相关产品推荐

