反応の新しいフック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
が変更されたときにエフェクトを呼び出すonlyの目的を完全に無効にします。他の小道具や参照が変更されたときに呼び出されたくありません。
ここで、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
以外のものが変化したときにアニメーションを回避することもできます。
依存関係の配列にprops.onAnimationComplete
を含めることにより、lintルールを無効にすることを避け、欠落している依存関係に関連する将来のバグを導入しないようにすることができます。
これが実際の例です:
コールバックで新しい(古くない)必要がありますが、効果を再発火してはならない値を参照に移動します。
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
の戻り値が変化しないため、機能します。
それはあなたに悪いアドバイスを与えるので、リンターを抑制してください。 Reactを使用すると、2番目の引数に、変更(および変更のみ)によって効果の発動をトリガーする必要がある値を渡す必要があります。
useEffect(() => {
animateSomething(ref, props.onAnimationComplete);
}, [props.foo]); // eslint-disable-line react-hooks/exhaustive-deps
Ryan's solution と同じ結果になります。
私はこのリンター規則に違反しても問題はないと思います。 useCallback
とuseMemo
とは対照的に、通常の場合はエラーにはなりません。 2番目の引数の内容は、高レベルのロジックです。
無関係な値が変化したときにエフェクトを呼び出すこともできます。
useEffect(() => {
alert(`Hi ${props.name}, your score is changed`);
}, [props.score]);