React hook useEffect 依赖数组
React hook useEffect dependency array
我正在努力思考 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
将其标记为错误。效果中使用的所有依赖项都应在 dependencies 数组中声明。所以这意味着我必须执行以下操作:
useEffect(() => {
animateSomething(ref, props.onAnimationComplete);
}, [props.foo, ref, props.onAnimationComplete]);
这不会导致 linting 错误,但它完全违背了 仅 在 props.foo
更改时调用效果的目的。我不想在其他道具或裁判发生变化时调用它。
现在,我阅读了一些关于使用 useCallback
来包装它的内容。我试过了,但没有进一步。
有人可以帮忙吗?
我建议这样写:
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
以外的事情发生变化时进行动画处理。
通过在 dependencies 数组中包含 props.onAnimationComplete
,您可以避免禁用 lint 规则,这有助于确保您不会引入与缺少依赖项相关的未来错误。
这是一个工作示例:
抑制 linter,因为它给了你一个糟糕的建议。 React 要求您将值传递给第二个参数,这些值(并且只有哪些)更改必须触发效果。
useEffect(() => {
animateSomething(ref, props.onAnimationComplete);
}, [props.foo]); // eslint-disable-line react-hooks/exhaustive-deps
它导致与 相同的结果。
我认为违反此 linter 规则没有问题。与 useCallback
和 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
return 值在渲染时不会改变。
我正在努力思考 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
将其标记为错误。效果中使用的所有依赖项都应在 dependencies 数组中声明。所以这意味着我必须执行以下操作:
useEffect(() => {
animateSomething(ref, props.onAnimationComplete);
}, [props.foo, ref, props.onAnimationComplete]);
这不会导致 linting 错误,但它完全违背了 仅 在 props.foo
更改时调用效果的目的。我不想在其他道具或裁判发生变化时调用它。
现在,我阅读了一些关于使用 useCallback
来包装它的内容。我试过了,但没有进一步。
有人可以帮忙吗?
我建议这样写:
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
以外的事情发生变化时进行动画处理。
通过在 dependencies 数组中包含 props.onAnimationComplete
,您可以避免禁用 lint 规则,这有助于确保您不会引入与缺少依赖项相关的未来错误。
这是一个工作示例:
抑制 linter,因为它给了你一个糟糕的建议。 React 要求您将值传递给第二个参数,这些值(并且只有哪些)更改必须触发效果。
useEffect(() => {
animateSomething(ref, props.onAnimationComplete);
}, [props.foo]); // eslint-disable-line react-hooks/exhaustive-deps
它导致与
我认为违反此 linter 规则没有问题。与 useCallback
和 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
return 值在渲染时不会改变。