useEffectEvent

useEffectEvent 是一个 React Hook,它可以让你将 Effect 中的非响应式逻辑提取到一个可复用的函数中,这个函数称为 Effect Event

const onSomething = useEffectEvent(callback)

参考

useEffectEvent(callback)

在组件的顶层调用 useEffectEvent 来声明一个 Effect Event。Effect Event 是你可以在 Effect 中调用的函数,例如 useEffect

import { useEffectEvent, useEffect } from 'react';

function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('已连接!', theme);
});

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]);

// ...
}

在下方查看更多示例

参数

  • callback:一个包含你 Effect Event 逻辑的函数。当你使用 useEffectEvent 定义一个 Effect Event 时,callback 在被调用时总是可以访问到最新的 props 和 state。这有助于避免陈旧闭包问题。

返回值

返回一个 Effect Event 函数。你可以在 useEffectuseLayoutEffectuseInsertionEffect 中调用这个函数。

将强制执行此限制,以防止在错误的上下文中调用效果事件。

注意事项

  • 仅在 Effect 中调用:Effect Event 应该只在 Effect 中调用。在使用它的 Effect 之前定义它。不要将它传递给其他组件或 hooks。eslint-plugin-react-hooks linter(6.1.1 或者更高版本)将强制执行此限制,以防止在错误的上下文中调用 Effect Events。
  • 不是依赖数组的捷径:不要用 useEffectEvent 来避免在 Effect 的依赖数组中声明依赖。这可能会隐藏 bug 并让代码更难理解。更推荐显式依赖,或使用 ref 来比较之前的值。
  • 用于非响应式逻辑:仅在逻辑不依赖变化的值时使用 useEffectEvent 来提取。

用法

读取最新的 props 和 state

通常,当你在 Effect 中访问一个响应式值时,你必须把它包含在依赖数组里。这样可以确保当这个值改变时,Effect 会再次运行,这通常是期望的行为。

但在某些情况下,你可能只想在 Effect 中读取最新的 props 或 state,而不希望当这些值改变时让 Effect 重新运行。

要在 Effect 中读取最新的 props 或 state,而不让这些值成为响应式依赖,请把它们放进一个 Effect Event 中。

import { useEffect, useContext, useEffectEvent } from 'react';

function Page({ url }) {
const { items } = useContext(ShoppingCartContext);
const numberOfItems = items.length;

const onNavigate = useEffectEvent((visitedUrl) => {
logVisit(visitedUrl, numberOfItems);
});

useEffect(() => {
onNavigate(url);
}, [url]);

// ...
}

在本例中,当 url 发生变化时,Effect 应在呈现后重新运行(以记录新页面的访问),但当 numberOfItems 发生变化时,它 应该重新运行。通过将日志记录逻辑封装在一个 Effect 事件中,numberOfItems 就变成了非响应的。它总是从最新值读取,而不会触发 Effect。

你可以将 url 等响应式值作为参数传递给 Effect Event,使其保持响应状态,同时在事件内部访问最新的非响应式值。