理解 React 中详尽的 Eslint 规则

当我们在效果挂钩中缺少依赖项时,“react-hooks/exhaustive-deps”规则会警告我们。 要消除警告,请将函数或变量声明移动到 useEffect 钩子内,记住每次渲染时更改的数组和对象或禁用规则。

下面是一个如何导致警告的示例。

import React, {useEffect, useState} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  // 👇️ objects/arrays 不同于 re-renders
  // 它们是通过引用进行比较的 
  const obj = {country: 'China', city: 'Beijing'};

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');

    // ⛔️ React Hook useEffect has a missing dependency: 'obj'.
    // Either include it or remove the dependency array. eslintreact-hooks/exhaustive-deps
  }, []);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

问题是我们在 useEffect 钩子中使用了 obj 变量,但我们没有将它包含在依赖项数组中。

该错误最明显的解决方案是将 obj 变量添加到 useEffect 钩子的依赖项数组中。

但是,在这种情况下,它会导致错误,因为对象和数组在 JavaScript 中是通过引用进行比较的。

obj 变量是一个在每次重新渲染时都具有相同键值对的对象,但它每次都指向内存中的不同位置,因此它会导致相等性检查失败并导致无限的重新渲染循环。

在 JavaScript 中,数组也通过引用进行比较。

绕过警告“React Hook useEffect has a missing dependency”的一种方法是禁用单行或整个文件的 eslint 规则。

import React, {useEffect, useState} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  // 👇️ objects/arrays 不同于 re-renders
  const obj = {country: 'Germany', city: 'Hamburg'};

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

依赖数组上方的注释禁用了单行的 react-hooks/exhausting-deps 规则。

当 useEffect 钩子传递一个空数组作为第二个参数时,它仅在组件挂载时调用。

另一种解决方案是将变量或函数声明移动到 useEffect 钩子内。

import React, {useEffect, useState} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  useEffect(() => {
    // 👇️ 在 useEffect 钩子内移动对象/数组/函数声明
    const obj = {country: 'Germany', city: 'Hamburg'};

    setAddress(obj);
    console.log('useEffect called');
  }, []);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

我们将对象的变量声明移到了 useEffect 钩子中。

这消除了警告,因为钩子不再依赖于外部对象。

另一种可能的解决方案,很少使用,但很好了解,是将函数或变量声明移出组件。

import React, {useEffect, useState} from 'react';

// 👇️ 将函数/变量声明移到组件之外
const obj = {country: 'Germany', city: 'Hamburg'};

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');
  }, []);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

这很有帮助,因为每次重新渲染 App 组件时都不会重新创建变量。

该变量将在所有渲染中指向内存中的相同位置,因此 useEffect 不需要在其依赖项数组中跟踪它。

另一种解决方案是使用 useMemo 钩子来获取记忆值。

import React, {useMemo, useEffect, useState} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  // 👇️ 获取记住的值
  const obj = useMemo(() => {
    return {country: 'Germany', city: 'Hamburg'};
  }, []);

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');

    // 👇️ 安全地包含在依赖项数组中
  }, [obj]);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

我们使用 useMemo 钩子来获得一个在渲染之间不会改变的记忆值。

useMemo 钩子接受一个函数,该函数返回一个要记忆的值和一个依赖数组作为参数。 如果其中一个依赖项发生了变化,钩子只会重新计算记忆值。

如果你正在使用一个函数,你可以使用 useCallback 钩子来获得一个在渲染之间不会改变的记忆回调。

import React, {useMemo, useEffect, useState, useCallback} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  // 👇️ 获取记住的回调函数
  const sum = useCallback((a, b) => {
    return a + b;
  }, []);

  // 👇️ 获取记住的值
  const obj = useMemo(() => {
    return {country: 'Germany', city: 'Santiago'};
  }, []);

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');

    console.log(sum(100, 100));

    // 👇️ 安全地包含在依赖项数组中
  }, [obj, sum]);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}

useCallback 钩子采用内联回调函数和依赖项数组,并返回回调的记忆版本,仅当依赖项之一发生更改时才会更改。

如果没有任何建议适用于我们的用例,我们可以随时通过注释隐藏警告。

import React, {useEffect, useState} from 'react';

export default function App() {
  const [address, setAddress] = useState({country: '', city: ''});

  const obj = {country: 'Chile', city: 'Santiago'};

  useEffect(() => {
    setAddress(obj);
    console.log('useEffect called');

  // 👇️ 禁用单行规则

  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <div>
      <h1>Country: {address.country}</h1>
      <h1>City: {address.city}</h1>
    </div>
  );
}
免责声明:
1.本站所有内容由本站原创、网络转载、消息撰写、网友投稿等几部分组成。
2.本站原创文字内容若未经特别声明,则遵循协议CC3.0共享协议,转载请务必注明原文链接。
3.本站部分来源于网络转载的文章信息是出于传递更多信息之目的,不意味着赞同其观点。
4.本站所有源码与软件均为原作者提供,仅供学习和研究使用。
5.如您对本网站的相关版权有任何异议,或者认为侵犯了您的合法权益,请及时通知我们处理。
火焰兔 » 理解 React 中详尽的 Eslint 规则