移除 Effect 依赖

有时,你 确实 希望 Effect 对某个值"做出反应",但该值的变化比你希望的更频繁------并且可能不会从用户的角度反映任何实际变化。例如,假设你在组件中创建了 options 对象,然后从 Effect 内部读取该对象:

js 复制代码
function ChatRoom() {
  const options = {
	 serverUrl: 'https://localhost:1234',
	 roomId: '音乐'
  };
  const [message, setMessage] = useState('');

  useEffect(() => {
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [options]); // ✅ 所有依赖已声明
  // ...

在每次重新渲染 ChatRoom 组件时,都会从头开始创建一个新的 options 对象。React 发现 options 对象与上次渲染期间创建的 options 对象是 不同的对象。这就是为什么它会重新同步 Effect(依赖于 options),并且会在你输入时重新连接聊天。

此问题仅影响对象和函数。在 JavaScript 中,每个新创建的对象和函数都被认为与其他所有对象和函数不同。即使他们的值相同也没关系!

如果该对象不依赖于任何 props 和 state,你可以将该对象移到组件之外:

js 复制代码
const options = {
  serverUrl: 'https://localhost:1234',
  roomId: '音乐'
};

function ChatRoom() {
  const [message, setMessage] = useState('');

  useEffect(() => {
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, []); // ✅ 所有依赖已声明
  // ...

由于 createOptions 是在组件外部声明的,因此它不是响应式值。这就是为什么它不需要在 Effect 的依赖中指定,以及为什么它永远不会导致 Effect 重新同步。

将动态对象和函数移动到 Effect 中。如果对象依赖于一些可能因重新渲染而改变的响应式值,例如 roomId props,那么你不能将它放置于组件 外部。你可以在 Effect 内部 创建它:

js 复制代码
const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');

  useEffect(() => {
    function createOptions() {
      return {
        serverUrl: serverUrl,
        roomId: roomId
      };
    }

    const options = createOptions();
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // ✅ 所有依赖已声明
  // ...

你可以编写自己的函数来组织 Effect 中的逻辑。只要将这些函数声明在 Effect 内部,它们就不是响应式值,因此它们也不是 Effect 的依赖。

从对象中读取原始值 ,有时,你可能会通过 props 接收到类型为对象的值:

js 复制代码
function ChatRoom({ options }) {
  const [message, setMessage] = useState('');

  useEffect(() => {
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [options]); // ✅ 所有依赖已声明
  // ...

这将导致 Effect 在每次父组件重新渲染时重新连接。要解决此问题,请从 Effect 外部 读取对象信息,并避免依赖对象和函数类型

js 复制代码
function ChatRoom({ options }) {
  const [message, setMessage] = useState('');

  const { roomId, serverUrl } = options;
  useEffect(() => {
    const connection = createConnection({
      roomId: roomId,
      serverUrl: serverUrl
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ✅ 所有依赖已声明
  // ...

从函数中计算原始值。同样的方法也适用于函数。例如,假设父组件传递了一个函数:

为避免使其成为依赖(并导致它在重新渲染时重新连接),请在 Effect 外部调用它。这为你提供了不是对象的 roomId 和 serverUrl 值,你可以从 Effect 中读取它们:

js 复制代码
function ChatRoom({ getOptions }) {
  const [message, setMessage] = useState('');

  const { roomId, serverUrl } = getOptions();
  useEffect(() => {
    const connection = createConnection({
      roomId: roomId,
      serverUrl: serverUrl
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ✅ 所有依赖已声明
  // ...

这仅适用于 纯 函数,因为它们在渲染期间可以安全调用。如果函数是一个事件处理程序,但你不希望它的更改重新同步 Effect,将它包装到 Effect Event 中。

摘要

  1. 依赖应始终与代码匹配。
  2. 当你对依赖不满意时,你需要编辑的是代码。
  3. 抑制 linter 会导致非常混乱的错误,你应该始终避免它。
  4. 要移除依赖,你需要向 linter "证明"它不是必需的。
  5. 如果某些代码是为了响应特定交互,请将该代码移至事件处理的地方。
  6. 如果 Effect 的不同部分因不同原因需要重新运行,请将其拆分为多个 Effect。
  7. 如果你想根据以前的状态更新一些状态,传递一个更新函数。
  8. 如果你想读取最新值而不"反应"它,请从 Effect 中提取出一个 Effect Event。
  9. 在 JavaScript 中,如果对象和函数是在不同时间创建的,则它们被认为是不同的。
  10. 尽量避免对象和函数依赖。将它们移到组件外或 Effect 内。
相关推荐
technology_x14 分钟前
2026年财务报表分析软件测评:兼容与安全解析
java·服务器·前端
程序员鱼皮25 分钟前
Claude Opus 5 全新发布,7 大项目实测,夯还是拉?半价吊打 Fable 5?
前端·后端·ai编程
极简前端打杂工31 分钟前
从0到1搭建通用低代码平台(-)— 表单设计器
前端·全栈
吃饺子不吃馅31 分钟前
那就和前端好好道个别吧
前端
_瑞33 分钟前
试图教会你用 Xcode Instruments
前端·ios·xcode
海带紫菜菠萝汤1 小时前
WebCodecs API 实战:浏览器原生视频编解码的原理与性能测试
前端·javascript·音视频·视频编解码
一位正在转型AI全栈的前端工程师1 小时前
AI 全栈学习之旅 - Week2:从零搭建一个可部署的 AI 聊天应用
前端
aixingpan2 小时前
aixingpan.cn API开发文档:api_docs_trichart_natal_solararc_transit2接口指南
前端·php
Ayayoyo2 小时前
公平随机转盘的前端实现:Web Crypto API、拒绝采样与加权抽取
前端
GitLqr2 小时前
提升 Web 开发效率:我常用的 10 个 TypeScript 实战技巧
vue.js·react.js·typescript