我试图把头缠在新的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
进行包装的内容。我尝试过,但没有得到进一步的解决。
有人可以帮忙吗?
答案 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规则,该规则有助于确保您以后不会引入与缺少依赖项有关的错误。
这是一个可行的示例:
答案 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的结果相同。
我认为违反这条短绒规则没有问题。与useCallback
和useMemo
相比,通常情况下不会导致错误。第二个参数的内容是高级逻辑。
您甚至可能希望在外部值发生变化时调用效果:
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 */