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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:56:34