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
相关产品推荐
相关产品推荐

