gpt4 book ai didi

reactjs - 为什么从 React 的 useEffect 依赖列表中省略函数是不安全的?

转载 作者:行者123 更新时间:2023-12-04 15:36:44 25 4
gpt4 key购买 nike

根据React Hook FAQ ,

It is only safe to omit a function from the dependency list if nothing in it (or the functions called by it) references props, state, or values derived from them.



然后,FAQ 继续给出一个例子,其中省略了该函数并说该代码包含一个错误。但是,FAQ 从未提及错误是什么。

我做了一个类似的例子,我创建了一个使用两个状态的函数。然后从一个 useEffect 钩子(Hook)调用该函数,该钩子(Hook)在其依赖项列表中只有一个状态。然而,即使有 promise 的 ESLint 缺少依赖项的警告,该函数和 useEffect 钩子(Hook)仍按预期工作。

Code sandbox of the example

预期语义:
  • 单击问候按钮时显示警报(直接函数调用)
  • 当“问候”状态改变时显示警报(通过 useEffect)
  • 名称更改时不显示警报。
  • 每当显示问候语时,都会使用最后指定的名称。

  • 代码:
    export function UseEffectEx(props) {
    const [greeting, setGreeting] = useState("Hello");
    const [name, setName] = useState("John");
    const [randomNumber, setRandomNumber] = useState(Math.random());

    function greet() {
    alert(`${greeting}, ${name}.`);
    }

    useEffect(
    function greetOnGreetingChange() {
    greet();
    },
    [greeting]
    );

    return (
    <div>
    <button onClick={greet}>Greet</button>
    <button onClick={() => setGreeting("Hello")}>
    set greeting to 'Hello'
    </button>
    <button onClick={() => setGreeting("Goodbye")}>
    set greeting to 'Goodbye'
    </button>
    <button onClick={() => setName("John")}>set name to 'John'</button>
    <button onClick={() => setName("Jane")}>set name to 'Jane'</button>
    <button onClick={() => setRandomNumber(Math.random())}>
    generate random
    </button>
    <p>Random number = ${randomNumber}</p>
    </div>
    );
    }


    所有预期的语义都得到满足。 Curcially,使用按钮更改名称状态不会触发警报,但触发警报时始终使用正确的名称。

    ESLint 警告

    上面的代码产生了 promise 的 react 钩子(Hook)/穷尽-deps useEffect() 的依赖项列表上的警告。警告说 tat 钩子(Hook)缺少 greet() 的依赖项.警告的自动修复是添加 greet 作为依赖项。
      useEffect(
    function greetOnGreetingChange() {
    greet();
    },
    [greeting, greet]
    );

    然而,这会产生另一个 ESLint 错误,这次是在 greet() 上。功能。该错误表明该函数在每次渲染时都被调用。单击生成随机按钮可确认此意外行为。 ESLint 建议使用 greet()函数应该包含在 useCallback 中效果,例如:
      const greet = useCallback(function greet() {
    alert(`${greeting}, ${name}.`)
    }, [greeting]);

    但是在海龟的所有场景中,ESLint 提示 useCallback 效果缺少 name依赖性。添加该依赖项会破坏预期的语义,因为警报现在将在名称状态更新时触发。

    解决方案?

    这是一个简单的、有点人为的例子,但它经常出现在我一直在研究的多个代码库中。场景很简单。您在组件内使用函数有一些状态。该函数在组件内的多个位置被调用,在 useEffect 钩子(Hook)内部和外部。您希望 useEffect 钩子(Hook)仅在单个 prop 或 state 更改时调用该函数。

    React 的文档建议最好的解决方案是将函数移动到 useEffect 钩子(Hook)中。但这会阻止它在组件内的其他地方使用。下一个建议是将 fucntion 包含在依赖项列表中,并在需要时使用 useCallback() 钩子(Hook)包装它。然而,在许多情况下,这要么引入了不受欢迎的行为,要么只是将 ESLint 错误引导至 useCallback()。

    React 想要防范的原始代码中的“bug”是什么?除了禁用 ESLint 检查之外,还有其他解决方案吗?

    最佳答案

    基于 Dan Abramov Use Effect article (由@Bennett Dams 提供),没有很好的解决方案。

    问题:代码中的错误是什么?

    ESList 警告要防范的错误是,更改名称状态时不会触发效果。然而,由于这种行为是有意为之,更大的问题是代码的语义取决于状态变化,有时有时会导致更新,有时则不会。这似乎与 React Hooks 的潮流背道而驰。

    问:好的,但我还是想做。除了禁用 ESLint 警告之外,还有其他解决方案吗?

    是的 - 虽然有点难看,但使用 useReducer可以达到想要的语义。下面的代码很难看,因为我们使用了一个 reducer 来触发一个函数,而不是按预期使用它,即更新状态。

    function Example(props) {
    const [greeting, setGreeting] = useState('Hello');
    const [name, setName] = useState('John');
    const [randomNumber, setRandomNumber] = useState(Math.random());
    const [_, dispatch] = useReducer(reducer, {});

    function greet() {
    alert(`${greeting}, ${name}.`)
    }

    function reducer(state, action) {
    if (action.type === 'alert') {
    greet();
    }
    return state;
    }


    useEffect(function greetOnGreetingChange() {
    dispatch({type: 'alert'})
    }, [dispatch, greeting]);

    return (
    <div>
    <button onClick={() => setGreeting('Hello')}>set greeting to 'Hello'</button>
    <button onClick={() => setGreeting('Goodbye')}>set greeting to 'Goodbye'</button>
    <button onClick={() => setName('John')}>set name to 'John'</button>
    <button onClick={() => setName('Jane')}>set name to 'Jane'</button>
    <button onClick={() => setRandomNumber(Math.random())} >generate random</button>
    <button onClick={() => greet()}>greet</button>
    <p>Random number = ${randomNumber}</p>

    </div>
    )
    }

    dispatch保证在渲染中始终保持一致,效果只会在问候状态更改时触发,而警报仍将捕获并显示名称状态的任何更改。

    关于reactjs - 为什么从 React 的 useEffect 依赖列表中省略函数是不安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59491334/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com