React hook useEffect 依赖数组

IT技术 javascript reactjs react-hooks
2021-05-18 10:41:51

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

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

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

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

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

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

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

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

有人可以帮忙吗?

3个回答

我建议这样写:

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 规则,这有助于确保您不会引入与缺少依赖项相关的未来错误。

这是一个工作示例:

编辑动画

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

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

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

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

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

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

将回调中必须是新鲜的(不是陈旧的)但不能重新触发效果的值移动到 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]);

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