移除 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 内。
相关推荐
Bug-制造者41 分钟前
现代Web应用全栈开发:从架构设计到部署落地实战
前端
青春喂了后端1 小时前
IntelliGit 前端状态层重构:把一个全局 Store 拆成清晰的状态边界
前端·重构·状态模式
IT_陈寒2 小时前
Redis内存用爆了,原来我们都忽略了这个配置
前端·人工智能·后端
qq_2518364572 小时前
基于java Web汽车销售管理系统设计与实现
java·前端·汽车
花椒技术2 小时前
低代码平台接入 Agent 后,我们踩到的组件、上下文和追问坑
前端·人工智能·agent
豹哥学前端3 小时前
事件循环(Event Loop)深度解析:让你彻底搞懂 JS 的执行顺序
前端·javascript·面试
竹林8183 小时前
用 wagmi v2 + Next.js 14 搞 NFT 交易市场前端:从合约调用失败到顺利上架,我踩了哪些坑
javascript·next.js
前端不开发3 小时前
用一个 Bookmarklet(书签脚本),给任意网页挂一个可拖拽悬浮窗
前端·javascript