web-dev-qa-db-ja.com

ReactフックuseEffect依存関係配列

反応の新しいフック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を使用してこれをラップする方法について読みました。私はそれを試しましたが、それ以上は得られませんでした。

誰か助けてもらえますか?

35
overdub60

これを次のように書くことをお勧めします。

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ルールを無効にすることを避け、欠落している依存関係に関連する将来のバグを導入しないようにすることができます。

これが実際の例です:

Edit animate

19
Ryan Cogswell

コールバックで新しい(古くない)必要がありますが、効果を再発火してはならない値を参照に移動します。

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の戻り値が変化しないため、機能します。

0
Finesse

それはあなたに悪いアドバイスを与えるので、リンターを抑制してください。 Reactを使用すると、2番目の引数に、変更(および変更のみ)によって効果の発動をトリガーする必要がある値を渡す必要があります。

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

Ryan's solution と同じ結果になります。

私はこのリンター規則に違反しても問題はないと思います。 useCallbackuseMemoとは対照的に、通常の場合はエラーにはなりません。 2番目の引数の内容は、高レベルのロジックです。

無関係な値が変化したときにエフェクトを呼び出すこともできます。

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