You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React同一useRef绑定多组件,组件卸载后调用focus报错的解决咨询

问题描述

使用useRef钩子同时引用两个Input组件时,遇到组件卸载后useRef失效的问题:

  • 关闭Modal时,其内部的Input组件会被销毁,导致useRef的current值被重置为undefined
  • 此时点击外部剩余Input组件的按钮触发handleFocusInput,会抛出错误:TypeError: Cannot read properties of null (reading 'focus')

相关代码如下:

父组件代码

const ref = useRef();

const handleFocusInput = () => {
  ref.current.focus();
}

// 两个Input组件共用同一个useRef
<Input ref={ref} onFocusInput={handleFocusInput} />

// 关闭Modal时销毁内部Input组件
<Modal onClose={() => setCloseModal()}> 
  <Input ref={ref} onFocusInput={handleFocusInput} />
</Modal>

Input组件代码

const Input = forwardRef((props, ref) => {
   return (
     <>
       <button onClick={props.onFocusInput}>Click to focus input</button>
       <input ref={ref} type="text" />
     </>
   )
})
解决方案

方法1:为每个Input分配独立的ref

不要共用同一个ref,给每个Input创建单独的ref,并让按钮只触发对应Input的聚焦逻辑,同时增加空值判断避免报错。

修改后的父组件代码:

const inputRef1 = useRef();
const inputRef2 = useRef();

const handleFocusInput = (targetRef) => {
  if (targetRef.current) {
    targetRef.current.focus();
  }
}

<Input ref={inputRef1} onFocusInput={() => handleFocusInput(inputRef1)} />

<Modal onClose={() => setCloseModal()}> 
  <Input ref={inputRef2} onFocusInput={() => handleFocusInput(inputRef2)} />
</Modal>

这种方式逻辑清晰,每个按钮只操作对应Input的ref,即使其中一个Input被销毁,另一个的ref依然有效。

方法2:用集合维护活跃的Input ref

如果需要统一管理多个Input的状态,可以用集合保存当前挂载的Input实例,在组件挂载时添加ref,卸载时移除。

父组件修改:

const inputRefs = useRef(new Set());

const handleFocusInput = () => {
  // 示例:聚焦第一个活跃的Input,可根据需求调整逻辑
  const firstActiveRef = [...inputRefs.current][0];
  if (firstActiveRef) {
    firstActiveRef.focus();
  }
}

// 传递添加/移除ref的方法
const addRef = (ref) => {
  ref && inputRefs.current.add(ref);
}

const removeRef = (ref) => {
  ref && inputRefs.current.delete(ref);
}

Input组件修改:

const Input = forwardRef((props, ref) => {
  useEffect(() => {
    // 挂载时将ref加入集合
    props.addRef(ref.current);
    // 卸载时从集合移除ref
    return () => {
      props.removeRef(ref.current);
    };
  }, [props]);

  return (
    <>
      <button onClick={props.onFocusInput}>Click to focus input</button>
      <input ref={ref} type="text" />
    </>
  )
})

父组件使用Input:

<Input ref={useRef()} onFocusInput={handleFocusInput} addRef={addRef} removeRef={removeRef} />

<Modal onClose={() => setCloseModal()}> 
  <Input ref={useRef()} onFocusInput={handleFocusInput} addRef={addRef} removeRef={removeRef} />
</Modal>

这种方式适合批量操作多个Input的场景,集合中只会保留当前处于挂载状态的Input ref。

方法3:调用focus前增加空值判断(仅临时规避报错)

如果必须共用一个ref(不推荐,因为多个组件共用会导致ref被覆盖),可以在调用focus前先判断current是否存在:

const handleFocusInput = () => {
  if (ref.current) {
    ref.current.focus();
  }
}

注意:这种方法只能避免报错,无法解决ref被覆盖的问题——当Modal内的Input挂载时,ref会指向该组件,Modal关闭后ref变为undefined,此时外部Input的按钮也无法聚焦对应的输入框。

内容的提问来源于stack exchange,提问作者Epple

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 06:32:11