web-dev-qa-db-ja.com

ReactフックuseEffectでoldValuesとnewValuesを比較する方法は?

たとえば、rate、sendAmount、およびreceiveAmountの3つの入力があるとします。 useEffect diffing paramsに3つの入力を配置しました。ルールは次のとおりです。

  • SendAmountが変更された場合、receiveAmount = sendAmount * rateを計算します
  • ReceiveAmountが変更された場合、sendAmount = receiveAmount / rateを計算します
  • レートが変更された場合、receiveAmount = sendAmount * rateのときにsendAmount > 0を計算するか、sendAmount = receiveAmount / rateのときにreceiveAmount > 0を計算します

問題を示すためのcodesandbox https://codesandbox.io/s/pkl6vn7x6j を次に示します。

この場合、3つのハンドラーを作成する代わりに、oldValuesのようなnewValuescomponentDidUpdateを比較する方法はありますか?

ありがとう


usePrevioushttps://codesandbox.io/s/30n01w2r06 を使用した最終的なソリューションを次に示します。

この場合、各変更が同じネットワーク呼び出しにつながるため、複数のuseEffectを使用できません。だからこそ、changeCountを使用して変更も追跡します。このchangeCountは、ローカルからの変更のみを追跡するのにも役立ちます。そのため、サーバーからの変更による不必要なネットワーク呼び出しを防ぐことができます。

42
rwinzhang

カスタムフックを記述して、 useRef を使用して以前の小道具を提供できます。

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

useEffectで使用します

const Component = (props) => {
    const {receiveAmount, sendAmount } = props
    const prevAmount = usePrevious({receiveAmount, sendAmount});
    useEffect(() => {
        if(prevAmount.receiveAmount !== receiveAmount) {

         // process here
        }
        if(prevAmount.sendAmount !== sendAmount) {

         // process here
        }
    }, [receiveAmount, sendAmount])
}

ただし、2つのuseEffectを別々に処理する変更IDごとに別々に使用する場合、読みやすく、理解しやすくなります。

62
Shubham Khatri

オプション1-useCompareフック

現在の値を以前の値と比較することは一般的なパターンであり、実装の詳細を隠す独自のカスタムフックを正当化します。

const useCompare = (val: any) => {
    const prevVal = usePrevious(val)
    return prevVal !== val
}

const usePrevious = (value) {
    const ref = useRef();
    useEffect(() => {
      ref.current = value;
    });
    return ref.current;
}

const Component = (props) => {
  ...
  const hasVal1Changed = useCompare(val1)
  const hasVal2Changed = useCompare(val2);
  useEffect(() => {
    if (hasVal1Changed ) {
      console.log("val1 has changed");
    }
    if (hasVal2Changed ) {
      console.log("val2 has changed");
    }
  });

  return <div>...</div>;
};

デモ

オプション2-値が変更されたときにuseEffectを実行する

const Component = (props) => {
  ...
  useEffect(() => {
    console.log("val1 has changed");
  }, [val1]);
  useEffect(() => {
    console.log("val2 has changed");
  }, [val2]);

  return <div>...</div>;
};

デモ

6
Ben Carp

状態は機能コンポーネントのコンポーネントインスタンスと密接に結合されていないため、useEffectなどで最初に保存しない限り、useRefで以前の状態に到達することはできません。これは、以前の状態がsetState updater関数内で利用可能であるため、状態更新が間違った場所に誤って実装された可能性があることも意味します。

これは、Reduxのようなストアを提供し、それぞれのパターンを実装できるuseReducerの適切な使用例です。状態の更新は明示的に実行されるため、どの状態プロパティが更新されるかを把握する必要はありません。これは、ディスパッチされたアクションからすでに明らかです。

は次のようになります。

function reducer({ sendAmount, receiveAmount, rate }, action) {
  switch (action.type) {
    case "sendAmount":
      sendAmount = action.payload;
      return {
        sendAmount,
        receiveAmount: sendAmount * rate,
        rate
      };
    case "receiveAmount":
      receiveAmount = action.payload;
      return {
        sendAmount: receiveAmount / rate,
        receiveAmount,
        rate
      };
    case "rate":
      rate = action.payload;
      return {
        sendAmount: receiveAmount ? receiveAmount / rate : sendAmount,
        receiveAmount: sendAmount ? sendAmount * rate : receiveAmount,
        rate
      };
    default:
      throw new Error();
  }
}

function handleChange(e) {
  const { name, value } = e.target;
  dispatch({
    type: name,
    payload: value
  });
}

...
const [state, dispatch] = useReducer(reducer, {
  rate: 2,
  sendAmount: 0,
  receiveAmount: 0
});
...
1
Estus Flask

誰かがusePrevious:のTypeScriptバージョンを探している場合

import { useEffect, useRef } from "react";

const usePrevious = <T>(value: T) => {
  const ref = useRef<T>();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};
0
SeedyROM