Javascript React hook useEffect 依赖数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/55228102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:15:59  来源:igfitidea点击:

React hook useEffect dependency array

javascriptreactjsreact-hooks

提问by overdub60

I trying to wrap my head around the new hooks api of react. Specifically, I'm trying to construct the classic use case that once was the following:

我试图将我的头围绕在 react 的新 hooks api 上。具体来说,我正在尝试构建曾经如下的经典用例:

componentDidUpdate(prevProps) {
    if (prevProps.foo !== this.props.foo) {
        // animate dom elements here...
        this.animateSomething(this.ref, this.props.onAnimationComplete);
    }
}

Now, I tried to build the same with a function component and useEffect, but can't figure out how to do it. This is what I tried:

现在,我尝试使用函数组件 and 构建相同的组件useEffect,但不知道该怎么做。这是我尝试过的:

useEffect(() => {
    animateSomething(ref, props.onAnimationComplete);
}, [props.foo]);

This way, the effect is only called when props.foo changes. And that does work – BUT! It appears to be an anti-pattern since the eslint-plugin-react-hooksmarks this as an error. All dependencies that are used inside the effect should be declared in the dependencies array. So that means I would have to do the following:

这样,效果只在 props.foo 改变时调用。这确实有效——但是!这似乎是一种反模式,因为将其eslint-plugin-react-hooks标记为错误。效果中使用的所有依赖项都应在依赖项数组中声明。所以这意味着我必须执行以下操作:

useEffect(() => {
    animateSomething(ref, props.onAnimationComplete);
}, [props.foo, ref, props.onAnimationComplete]);

That does not lead to the linting error BUT it totally defeats the purpose of onlycalling the effect when props.foochanges. I don't WANT it to be called when the other props or the ref change.

这不会导致 linting 错误,但它完全违背了props.foo更改时调用效果的目的。我不想在其他道具或 ref 更改时调用它。

Now, I read something about using useCallbackto wrap this. I tried it but didn't get any further.

现在,我读了一些关于 usinguseCallback来包装它的内容。我试过了,但没有进一步。

Can somebody help?

有人可以帮忙吗?

采纳答案by Ryan Cogswell

I would recommend writing this as follows:

我建议这样写:

const previousFooRef = useRef(props.foo);

useEffect(() => {
    if (previousFooRef.current !== props.foo) {
       animateSomething(ref, props.onAnimationComplete);
       previousFooRef.current = props.foo;
    }
}, [props.foo, props.onAnimationComplete]);

You can't avoid the complexity of having a condition inside the effect, because without it you will run your animation on mount rather than just when props.foochanges. The condition also allows you to avoid animating when things other than props.foochange.

您无法避免在效果中设置条件的复杂性,因为如果没有它,您将在安装时运行动画,而不仅仅是在props.foo更改时运行。该条件还允许您避免在其他事情props.foo发生变化时制作动画。

By including props.onAnimationCompletein the dependencies array, you avoid disabling the lint rule which helps ensure that you don't introduce future bugs related to missing dependencies.

通过包含props.onAnimationComplete在依赖项数组中,您可以避免禁用 lint 规则,这有助于确保您不会引入与缺少依赖项相关的未来错误。

Here's a working example:

这是一个工作示例:

Edit animate

编辑动画

回答by Finesse

Suppress the linter because it gives you a bad advice. React requires you to pass to the second argument the values which (and only which) changes must trigger an effect fire.

禁止 linter,因为它会给你一个不好的建议。React 要求您将值传递给第二个参数,哪些(并且只有哪些)更改必须触发效果触发。

useEffect(() => {
    animateSomething(ref, props.onAnimationComplete);
}, [props.foo]); // eslint-disable-line react-hooks/exhaustive-deps

It leads to the same result as the Ryan's solution.

它导致与Ryan 的解决方案相同的结果。

I see no problems with violating this linter rule. In contrast to useCallbackand useMemo, it won't lead to errors in common case. The content of the second argument is a high level logic.

我认为违反此 linter 规则没有问题。与useCallbackand 相比useMemo,它在一般情况下不会导致错误。第二个参数的内容是一个高级逻辑。

You may even want to call an effect when an extraneous value changes:

您甚至可能想要在无关值更改时调用效果:

useEffect(() => {
    alert(`Hi ${props.name}, your score is changed`);
}, [props.score]);

回答by Finesse

Move the values, that must be fresh (not stale) in the callback but mustn't refire the effect, to refs:

将回调中必须是新鲜的(不是陈旧的)但不能重新触发效果的值移动到 refs:

const elementRef = useRef(); // Ex `ref` from the question
const animationCompleteRef = useRef();

animationCompleteRef.current = props.onAnimationComplete;

useEffect(() => {
    animateSomething(elementRef, animationCompleteRef.current);
}, [props.foo, elementRef, animationCompleteRef]);

It works because useRefreturn value doesn't change on renders.

它有效是因为useRef返回值在渲染时不会改变。