移除 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 内。
相关推荐
码云之上2 小时前
从一个截图函数到一个 npm 包——pdf-snapshot 的诞生记
前端·node.js·github
码事漫谈3 小时前
AI提效,到底能强到什么程度?
前端·后端
IT_陈寒3 小时前
React hooks依赖数组这个坑差点把我埋了
前端·人工智能·后端
阿祖zu3 小时前
内容创作 AI 透明化声明倡议与项目开源
前端·后端·github
lpfasd1233 小时前
TypeScript + Cloudflare 全家桶部署项目全流程
前端·javascript·typescript
ZC跨境爬虫3 小时前
极验滑动验证码自动化实战:背景提取、缺口定位与Playwright滑动模拟
前端·爬虫·python·自动化
前端Hardy4 小时前
字节/腾讯内部流出!Claude Code 2026王炸玩法!效率暴涨10倍
前端·javascript·vue.js
糟糕好吃4 小时前
AI 全流程解析(LLM / Token / Context / RAG / Prompt / Tool / Skill / Agent)
前端·后端·设计模式
快手技术4 小时前
快手广告系统全面迈入生成式推荐时代!GR4AD:从Token到Revenue的全链路重构
前端·后端