React多输入框重渲染后焦点丢失:useRef使用问题求助
React多输入框重渲染后保持焦点的解决方案
一、最优解:避免不必要的重渲染(推荐)
问题根源是父组件更新state导致无关子组件也重渲染,进而销毁重建输入框丢失焦点。用React.memo包裹子组件,配合稳定的key,让子组件仅在props变化时重渲染。
子组件实现(React.memo优化)
import React, { memo } from 'react'; type InputProps = { fixtureId: string; score: number; onScoreChange: (id: string, value: number) => void; }; // React.memo让组件仅在props浅比较变化时重渲染 const ScoreInput = memo(({ fixtureId, score, onScoreChange }: InputProps) => { return ( <input type="number" value={score} onChange={(e) => onScoreChange(fixtureId, Number(e.target.value))} min={0} max={10} /> ); }); export default ScoreInput;
父组件使用
确保每个子组件的key唯一且稳定(用fixture.id),React会复用组件实例,不会销毁输入框:
import ScoreInput from './ScoreInput'; // 父组件state const [scores, setScores] = React.useState<Record<string, number>>({}); const handleScoreChange = (id: string, value: number) => { setScores(prev => ({ ...prev, [id]: value })); }; // 渲染子组件 {fixtures.map(fixture => ( <ScoreInput key={fixture.id} fixtureId={fixture.id} score={scores[fixture.id] || 0} onScoreChange={handleScoreChange} /> ))}
这种方式下,只有被修改的输入框对应的子组件会重渲染,且组件实例被复用,输入框不会丢失焦点。
二、强制重渲染时恢复焦点(备选)
如果业务逻辑必须让子组件重渲染,可通过正确管理ref来恢复焦点。
方式1:子组件独立维护ref
每个子组件自己保存输入框的ref,在props更新(重渲染)后自动聚焦:
import React, { useRef, useEffect } from 'react'; type InputProps = { fixtureId: string; score: number; onScoreChange: (id: string, value: number) => void; }; const ScoreInput = ({ fixtureId, score, onScoreChange }: InputProps) => { const inputRef = useRef<HTMLInputElement>(null); // 当score更新触发重渲染后,恢复焦点 useEffect(() => { inputRef.current?.focus(); }, [score]); return ( <input ref={inputRef} type="number" value={score} onChange={(e) => onScoreChange(fixtureId, Number(e.target.value))} min={0} max={10} /> ); }; export default ScoreInput;
方式2:父组件统一管理所有输入框ref
在父组件中维护一个ref对象存储所有输入框实例,更新state后找到对应输入框聚焦:
import React, { useRef, useEffect } from 'react'; const ParentComponent = () => { const [scores, setScores] = React.useState<Record<string, number>>({}); const inputRefs = useRef<Record<string, HTMLInputElement | null>>({}); const lastEditedId = useRef<string | null>(null); const handleScoreChange = (id: string, value: number) => { lastEditedId.current = id; setScores(prev => ({ ...prev, [id]: value })); }; // state更新、DOM渲染完成后恢复焦点 useEffect(() => { if (lastEditedId.current) { inputRefs.current[lastEditedId.current]?.focus(); } }, [scores]); return ( <div> {fixtures.map(fixture => ( <input key={fixture.id} ref={(el) => inputRefs.current[fixture.id] = el} type="number" value={scores[fixture.id] || 0} onChange={(e) => handleScoreChange(fixture.id, Number(e.target.value))} min={0} max={10} /> ))} </div> ); };
你之前代码的问题
你尝试用单个useRef存储多个输入框,但每个子组件的ref={ref}会覆盖ref.current的值,最终ref.current只会指向最后一个渲染的输入框,自然无法聚焦到对应id的输入框。需要为每个输入框分配独立的ref,或者用对象/数组统一管理多个ref。
内容的提问来源于stack exchange,提问作者artworkjpm
相关产品推荐
相关产品推荐

