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

React自定义Hook更新useState时如何避免组件重渲染及输入失焦

问题原因

每次调用useCustomHook(也就是使用该Hook的组件重渲染时),InputComponent都会被重新定义成一个全新的函数。React通过引用来判断组件类型,因此会把新的InputComponent视为完全不同的组件,直接卸载旧的输入框实例并挂载新实例——这就是输入框失去焦点的核心原因。

另外,validarTexto函数也会在每次Hook执行时重新创建,虽不会直接导致焦点丢失,但会造成不必要的性能开销。

解决思路

1. 把组件移出Hook,通过props传递状态(推荐)

将InputComponent定义在Hook外部,让它通过props接收状态值和变更回调,保证组件引用始终稳定,不会因Hook内部状态更新被重新创建:

import React, { useState } from 'react';

// 单独定义输入组件,通过props接收数据
const InputComponent = ({ value, onChange }) => {
  return <input type="text" value={value} onChange={onChange} />;
};

const useCustomHook = () => {
  const [textoDelInput, setTextoDelInput] = useState('');

  const validarTexto = () => {
    return textoDelInput !== '';
  };

  const handleChange = (event) => {
    setTextoDelInput(event.target.value);
  };

  // 返回组件、状态、回调和校验函数
  return {
    InputComponent,
    inputValue: textoDelInput,
    handleChange,
    validarTexto
  };
};

// 使用示例
const App = () => {
  const { InputComponent, inputValue, handleChange, validarTexto } = useCustomHook();
  return (
    <div>
      <InputComponent value={inputValue} onChange={handleChange} />
      <button onClick={() => console.log(validarTexto())}>校验</button>
    </div>
  );
};

2. 用useMemo缓存内部组件

如果必须在Hook内部定义组件,可以用useMemo包裹InputComponent,同时用useCallback缓存回调函数,确保组件引用稳定:

import React, { useState, useMemo, useCallback } from 'react';

const useCustomHook = () => {
  const [textoDelInput, setTextoDelInput] = useState('');

  // 用useCallback缓存校验函数,避免重复创建
  const validarTexto = useCallback(() => {
    return textoDelInput !== '';
  }, [textoDelInput]);

  // 用useCallback缓存change回调,保证引用稳定
  const handleChange = useCallback((event) => {
    setTextoDelInput(event.target.value);
  }, []);

  // 用useMemo缓存组件,只有依赖变化时才重新创建
  const InputComponent = useMemo(() => {
    return () => (
      <input type="text" value={textoDelInput} onChange={handleChange} />
    );
  }, [textoDelInput, handleChange]);

  return [validarTexto, InputComponent];
};

3. 改用组合组件封装逻辑

如果需要同时封装组件和状态逻辑,更推荐直接创建组合组件,而非在Hook里返回组件,这更符合React的设计理念:

import React, { useState } from 'react';

const InputWithValidation = ({ children }) => {
  const [textoDelInput, setTextoDelInput] = useState('');

  const validarTexto = () => {
    return textoDelInput !== '';
  };

  const handleChange = (event) => {
    setTextoDelInput(event.target.value);
  };

  // 通过render prop把校验函数暴露给父组件
  return (
    <div>
      <input type="text" value={textoDelInput} onChange={handleChange} />
      {typeof children === 'function' && children(validarTexto)}
    </div>
  );
};

// 使用示例
const App = () => {
  return (
    <InputWithValidation>
      {(validar) => (
        <button onClick={() => console.log(validar())}>校验</button>
      )}
    </InputWithValidation>
  );
};

内容的提问来源于stack exchange,提问作者Andreina Paola Riera

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:53:10