useEffect(setup, dependencies?)
- setup 函数选择性返回一个 清理(cleanup) 函数。当组件被添加到 DOM 的时候,React 将运行 setup 函数。在每次依赖项变更重新渲染后,React 将首先使用旧值运行 cleanup 函数(如果你提供了该函数),然后使用新值运行 setup 函数。在组件从 DOM 中移除后,React 将最后一次运行 cleanup 函数。
- dependencies setup 代码中引用的所有响应式值的列表。响应式值包括 props、state 以及所有直接在组件内部声明的变量和函数。如果你的代码检查工具 配置了 React,那么它将验证是否每个响应式值都被正确地指定为一个依赖项。React 将使用 Object.is 来比较每个依赖项和它先前的值。
import { useEffect } from 'react';
import { createConnection } from './chat.js';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}
Effect 只在客户端上运行,在服务端渲染中不会运行。
请在组件的顶层调用 useEffect可以监听 长链接,。还可以监听全局的浏览器事件、触发动画效果
import { useEffect } from 'react';
import { createConnection } from './chat.js';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}
如果你发现自己经常需要手动编写 Effect,那么这通常表明你需要为组件所依赖的通用行为提取一些 自定义 Hook。自定义封装hook
//test.hooks.tsx
import { useEffect } from 'react';
export function useTest({ dep }) {
useEffect(() => {
console.log('useTest');
}, [dep]);
}
//index.tsx
import { useTest } from './test.hooks';
export default function useHooks(props: Props, ref: any) {
useTest({ dep: currentUser });
}
控制非 React 小部件
有时,你希望外部系统与你组件的某些 props 或 state 保持同步。
使用 Effect 请求数据
直接在 Effect 中编写数据请求会显得重复,并且很难在以后添加缓存和服务端渲染等优化。使用自定义 Hook 更简单——不管是你自己的 Hook 还是由社区维护的 Hook。
如果你的 Effect 确实没有使用任何响应式值,则它仅在 初始渲染后 运行。
useEffect(() => {
// ...
}, []); // 不会再次运行(开发环境下除外)
如果完全不传递依赖数组,则 Effect 会在组件的 每次单独渲染(和重新渲染)之后 运行。
useEffect(() => {
// ...
}); // 总是再次运行
删除不必要的对象依赖项
如果你的 Effect 依赖于在渲染期间创建的对象或函数,则它可能会频繁运行。例如,此 Effect 在每次渲染后都重新连接,因为 options 对象 每次渲染都不同:
const serverUrl = 'https://localhost:1234';
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
const options = { // 🚩 这个对象在每次渲染时都是从头创建的
serverUrl: serverUrl,
roomId: roomId
};
useEffect(() => {
const connection = createConnection(options); // 它在 Effect 内部使用
connection.connect();
return () => connection.disconnect();
}, [options]); // 🚩 因此,这些依赖在重新渲染时总是不同的
// ...
删除不必要的函数依赖项
如果你的 Effect 依赖于在渲染期间创建的对象或函数,则它可能会频繁运行。例如,此 Effect 在每次渲染后重新连接,因为 createOptions 函数 在每次渲染时都不同:
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
function createOptions() { // 🚩 此函数在每次重新渲染都从头开始创建
return {
serverUrl: serverUrl,
roomId: roomId
};
}
useEffect(() => {
const options = createOptions(); // 它在 Effect 中被使用
const connection = createConnection();
connection.connect();
return () => connection.disconnect();
}, [createOptions]); // 🚩 因此,此依赖项在每次重新渲染都是不同的
// ...
useEffectEvent 这是实验性 API,并且在 React 的稳定版中还不可用。
在服务器和客户端上显示不同的内容(这个看官网,我没懂)
Effect 在组件挂载时运行了两次(这是在严格模式下的)
在开发环境下,如果开启严格模式,React 会在实际运行 setup 之前额外运行一次 setup 和 cleanup。
我的 Effect 做了一些视觉相关的事情,在它运行之前我看到了一个闪烁
如果 Effect 一定要阻止浏览器绘制屏幕,使用 useLayoutEffect 替换 useEffect。请注意,绝大多数的 Effect 都不需要这样。只有当在浏览器绘制之前运行 Effect 非常重要的时候才需要如此:例如,在用户看到 tooltip 之前测量并定位它。