たとえば、rate、sendAmount、およびreceiveAmountの3つの入力があるとします。 useEffect diffing paramsに3つの入力を配置しました。ルールは次のとおりです。
receiveAmount = sendAmount * rate
を計算しますsendAmount = receiveAmount / rate
を計算しますreceiveAmount = sendAmount * rate
のときにsendAmount > 0
を計算するか、sendAmount = receiveAmount / rate
のときにreceiveAmount > 0
を計算します問題を示すためのcodesandbox https://codesandbox.io/s/pkl6vn7x6j を次に示します。
この場合、3つのハンドラーを作成する代わりに、oldValues
のようなnewValues
とcomponentDidUpdate
を比較する方法はありますか?
ありがとう
usePrevious
https://codesandbox.io/s/30n01w2r06 を使用した最終的なソリューションを次に示します。
この場合、各変更が同じネットワーク呼び出しにつながるため、複数のuseEffect
を使用できません。だからこそ、changeCount
を使用して変更も追跡します。このchangeCount
は、ローカルからの変更のみを追跡するのにも役立ちます。そのため、サーバーからの変更による不必要なネットワーク呼び出しを防ぐことができます。
カスタムフックを記述して、 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ごとに別々に使用する場合、読みやすく、理解しやすくなります。
現在の値を以前の値と比較することは一般的なパターンであり、実装の詳細を隠す独自のカスタムフックを正当化します。
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>;
};
const Component = (props) => {
...
useEffect(() => {
console.log("val1 has changed");
}, [val1]);
useEffect(() => {
console.log("val2 has changed");
}, [val2]);
return <div>...</div>;
};
状態は機能コンポーネントのコンポーネントインスタンスと密接に結合されていないため、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
});
...
誰かがusePrevious:のTypeScriptバージョンを探している場合
import { useEffect, useRef } from "react";
const usePrevious = <T>(value: T) => {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
});
return ref.current;
};