Disons que j'ai 3 entrées: rate, sendAmount et receiveAmount. J'ai mis cela 3 entrées sur useEffect diffing params. Les règles sont:
receiveAmount = sendAmount * rate
sendAmount = receiveAmount / rate
receiveAmount = sendAmount * rate
lorsque sendAmount > 0
ou je calcule sendAmount = receiveAmount / rate
lorsque receiveAmount > 0
Voici les codesandbox https://codesandbox.io/s/pkl6vn7x6j pour illustrer le problème.
Existe-t-il un moyen de comparer les oldValues
et newValues
comme sur componentDidUpdate
au lieu de créer 3 gestionnaires pour ce cas?
Merci
Voici ma solution finale avec usePrevious
https://codesandbox.io/s/30n01w2r06
Dans ce cas, je ne peux pas utiliser plusieurs useEffect
, car chaque modification conduit au même appel réseau. C'est pourquoi j'utilise aussi changeCount
pour suivre le changement. Cette changeCount
est également utile pour suivre les modifications apportées uniquement en local, afin d'éviter des appels réseau inutiles en raison de modifications apportées par le serveur.
Vous pouvez écrire un crochet personnalisé pour vous fournir un accessoires précédents utilisant useRef
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
puis l'utiliser dans 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])
}
Cependant, sa lecture et sa compréhension sont plus claires et probablement meilleures et plus claires si vous utilisez deux useEffect
séparément pour chaque identificateur de modification que vous souhaitez traiter séparément.
Comparer une valeur actuelle à une valeur précédente est un modèle courant et justifie un crochet personnalisé qui cache les détails de la mise en œuvre.
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>;
};
Etant donné que l'état n'est pas étroitement associé à l'instance de composant dans les composants fonctionnels, l'état précédent ne peut pas être atteint dans useEffect
sans le sauvegarder au préalable, par exemple avec useRef
. Cela signifie également que la mise à jour d'état a peut-être été implémentée de manière incorrecte au mauvais endroit car l'état précédent est disponible dans la fonction setState
Updater.
C'est un bon cas d'utilisation pour useReducer
qui fournit un stockage de type Redux et permet d'implémenter un modèle respectif. Les mises à jour d'état sont effectuées explicitement. Il n'est donc pas nécessaire de déterminer quelle propriété d'état est mise à jour. cela ressort déjà de l'action expédiée.
Voici un exemple à quoi il pourrait ressembler:
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
});
...
Si n'importe qui est à la recherche d'une version TypeScript de usePrevious:
import { useEffect, useRef } from "react";
const usePrevious = <T>(value: T) => {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
});
return ref.current;
};