反应钩子useEffect依赖项数组

时间:2019-03-18 18:45:02

标签: javascript reactjs react-hooks

我试图把头缠在新的react钩子api上。具体来说,我正在尝试构建一次经典的用例:

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

现在,我尝试使用功能组件和useEffect构建相同的组件,但不知道如何实现。这是我尝试过的:

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

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

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

这不会导致掉毛错误,但会完全破坏props.foo更改时调用效果的目的。我不希望在其他道具或裁判改变时调用它。

现在,我读到一些有关使用useCallback进行包装的内容。我尝试过,但没有得到进一步的解决。

有人可以帮忙吗?

5 个答案:

答案 0 :(得分:5)

我建议这样编写:

const previousFooRef = useRef(props.foo);

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

您无法避免在效果中包含条件的复杂性,因为如果没有条件,您将在挂载而不是在props.foo更改时运行动画。该条件还使您可以避免在props.foo以外的其他事物发生变化时进行动画处理。

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

这是一个可行的示例:

Edit animate

答案 1 :(得分:1)

感谢瑞安,我弄清楚了。尽管我不得不说-至少在这种情况下-我看不出使用useEffect()优于传统的带有componentDidUpdate的React Component的好处。在我看来,钩子版本最终使它变得更加复杂。所以我可能会回到组件版本。

答案 2 :(得分:0)

抑制棉绒,因为它会给您一个不好的建议。 React要求您将第二个参数传递的值(并且只有哪个)必须触发效果触发。

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

Ryan's solution的结果相同。

我认为违反这条短绒规则没有问题。与useCallbackuseMemo相比,通常情况下不会导致错误。第二个参数的内容是高级逻辑。

您甚至可能希望在外部值发生变化时调用效果:

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

答案 3 :(得分:0)

将值(该值必须在回调中新鲜(不陈旧)但不能重新激活效果)移动到引用:

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

animationCompleteRef.current = props.onAnimationComplete;

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

之所以有效,是因为useRef的返回值在渲染器上没有变化。

答案 4 :(得分:-2)

ESLint无法理解您的代码或意图。您的代码应该可以正常工作。所以我建议禁用eslint:

/* eslint-disable */
useEffect(() => {
    animateSomething(ref, props.onAnimationComplete);
}, [props.foo]);
/* eslint-enable */