所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

别再“滥用” useState:这些高频需求更该交给 useRef

介绍

React 开发中,第一反应往往是用 stateprops 来驱动界面更新。但也并不是所有需求都需要触发渲染:很多时候,你只是想在组件内部“记住一些东西”或直接操作真实 DOM。

这个时候,useRef 才是更合适的工具。

  • 直接获取并操作 DOM(如 focus、scrollIntoView
  • 在渲染之间持久保存可变值而不触发重渲染(如计时器 ID)
  • 在多次渲染间保留“上一次”的信息(如上一次的 prop/state

什么是 useRef?

useRef 是一个 React Hook,它提供一个“可变对象”,更新它不会触发组件重新渲染。

const myRef = useRef(initialValue);

特性

  • 返回一个形如 { current: value } 的对象
  • 可通过 myRef.current = newValue 更新值
  • 值在渲染期间保持引用不变(稳定)
  • 改变 current 不会导致重新渲染

useRef 用于访问 DOM 元素

最常见的用例:获取 DOM 节点并调用原生方法。

例如,组件挂载时自动聚焦输入框:

import React, { useRef, useEffect } from "react";

function SearchInput() {
  const inputRef = useRef();

  useEffect(() => {
    inputRef.current.focus(); // Focus the input on mount
  }, []);

  return <input ref={inputRef} placeholder="Type to search..." />;
}

发生了什么?

  • inputRef.current 指向真实的 DOM 元素
  • 可直接调用 .focus().scrollIntoView() 等原生 API

这在以下场景尤为常见:

  • 登录/注册表单
  • 模态框首次打开时聚焦
  • 聊天输入框
  • 搜索栏与命令面板

useRef 存储可变状态(不重新渲染)

当你需要“记住一个值”,但又不希望它影响渲染时,useRef 是更合适的容器。

以下示例使用 setInterval 构建计时器:

import React, { useRef, useState, useEffect } from "react";

function Timer() {
  const [count, setCount] = useState(0);
  const intervalRef = useRef();

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setCount((prev) => prev + 1);
    }, 1000);

    return () => clearInterval(intervalRef.current);
  }, []);

  return <h1>Time: {count}s</h1>;
}

为什么不用 useState 存储 interval ID?

因为 useState 每次更新都会触发组件重新渲染。而计时器 ID 本身并不影响 UI,适合用 useRef 安全存放,避免不必要的渲染。

useRef 跟踪先前的值

另一个高频技巧:在多次渲染之间记录某个 propstate 的上一个值。

import React, { useEffect, useRef, useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = useRef();

  useEffect(() => {
    prevCount.current = count;
  }, [count]);

  return (
    <div>
      <p>Current: {count}</p>
      <p>Previous: {prevCount.current}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

适用场景包括:

  • 动画过渡中基于前后帧做插值
  • 对比 prop/state 的变化以触发特定逻辑
  • 撤销/重做等需要历史值的能力

useRef 与 useState — 何时使用?

特性useRefuseState
需要在渲染间隔期间保持持久
更改时触发重新渲染
存储 DOM 节点引用
跟踪值但不应影响 UI 渲染

经验法则

当你需要“记住一些东西”,但它不应驱动 UI 渲染或参与 diff 时,用 useRef;当它需要影响 UI(显示/隐藏、文本、样式等)时,用 useState

表单验证中的 useRef

在不需要根据输入过程中的变化实时更新 UI 时,表单可用 useRef 直接读取值:

function LoginForm() {
  const usernameRef = useRef();
  const passwordRef = useRef();

  const handleSubmit = (e) => {
    e.preventDefault();
    const username = usernameRef.current.value;
    const password = passwordRef.current.value;

    if (!username || !password) {
      alert("Both fields are required!");
    } else {
      alert(`Welcome, ${username}!`);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input ref={usernameRef} placeholder="Username" />
      <input ref={passwordRef} type="password" placeholder="Password" />
      <button type="submit">Login</button>
    </form>
  );
}

如果需要根据输入变化实时反馈校验或联动 UI,则应使用 useState 管理受控输入。

useRef 的注意事项

  • 它不是用来替代任何“影响 UI 的状态”
  • 更新 ref.current 不会通知 React,也不会触发渲染
  • 不应用它来控制“显示/隐藏 UI”等需要驱动视图变化的场景

总结

useRef 让你可以:

  • 直接访问 DOM 节点(如 .focus()
  • 在渲染间隔安全存储计时器或任意可变值
  • 追踪先前值,构建撤销/动画等增强能力
  • 编写更高性能、更稳定的 React 组件

它是 React 中的“低调英雄”。掌握它的边界与最佳实践,你会在恰当的地方用好它,并获得更流畅可靠的交互体验。

原文链接:https://code.ifrontend.net/archives/1375,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?