关于javascript:React hook useEffect依赖项数组

React hook useEffect dependency array

我试图用新的react钩子api包住我的头。具体来说,我正在尝试构建曾经是以下情况的经典用例:

1
2
3
4
5
6
componentDidUpdate(prevProps) {
    if (prevProps.foo !== this.props.foo) {
        // animate dom elements here...
        this.animateSomething(this.ref, this.props.onAnimationComplete);
    }
}

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

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

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

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

这不会导致掉毛错误,但完全无法实现仅在props.foo更改时调用效果的目的。我不希望在其他道具或ref更改时调用它。

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

有人可以帮忙吗?


我建议编写如下:

1
2
3
4
5
6
7
8
const previousFooRef = useRef(props.foo);

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

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

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

这是一个有效的示例:

Edit


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

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

得出的结果与Ryan的解决方案相同。

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

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

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


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

1
2
3
4
5
6
7
8
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的返回值在渲染器上没有变化。