移除 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 内。
相关推荐
小码哥_常19 小时前
安卓开发秘籍:解锁10大性能优化秘诀
前端
谁呛我名字20 小时前
JavaScript 类型转换与运算规则
javascript
try2find20 小时前
打印ascii码报错问题
java·linux·前端
郑州光合科技余经理21 小时前
同城O2O海外版二次开发实战:从支付网关到配送算法
开发语言·前端·后端·算法·架构·uni-app·php
冰暮流星21 小时前
javascript事件案例-全选框案例
服务器·前端·javascript
Dillon Dong1 天前
【系列主题】Next.js 16 + Turbopack 的暗礁:深入剖析 Tailwind v4 的 CSS 模块解析陷阱
javascript·css·容器·turbopack
Csvn1 天前
前端性能优化实战指南
前端
Moment1 天前
2026 年,AI 全栈时代到了,前端简历别再只写前端技术了 🫠🫠🫠
前端·后端·面试
糯米团子7491 天前
Web Worker
开发语言·前端·javascript
freewlt1 天前
React Server Components 深度解析
前端·react.js·前端框架