Jak porównać stare wartości i nowe wartości w Reactowym Hooku useEffect?
Załóżmy, że mam 3 wejścia: rate, sendAmount i receiveAmount. Umieściłem te 3 wejścia na useEffect diffing params. Zasady są następujące:
- Jeśli sendamount się zmienił, obliczam
receiveAmount = sendAmount * rate
- Jeśli receiveamount się zmienił, obliczam
sendAmount = receiveAmount / rate
- jeśli kurs się zmienił, obliczam
receiveAmount = sendAmount * rate
KiedysendAmount > 0
lub obliczamsendAmount = receiveAmount / rate
KiedyreceiveAmount > 0
Oto codesandbox https://codesandbox.io/s/pkl6vn7x6j aby zademonstrować problem.
Jest jest sposób, aby porównać oldValues
i newValues
Jak na componentDidUpdate
zamiast zrobić 3 obsługi dla tej sprawy?
Dzięki
Oto moje ostateczne rozwiązanie z usePrevious
https://codesandbox.io/s/30n01w2r06
W tym przypadku nie mogę użyć wielu useEffect
, ponieważ każda zmiana prowadzi do tego samego wywołania sieciowego. Dlatego też używam changeCount
do śledzenia zmian. To changeCount
również pomocne do śledzenia zmian tylko z lokalnego, więc mogę zapobiec niepotrzebnym połączeń sieciowych z powodu zmian na serwerze.
12 answers
Możesz napisać własny hook, aby dostarczyć poprzednie właściwości używając useRef
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
A następnie użyj go w 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])
}
Jednak jego jaśniejsze i prawdopodobnie lepsze i jaśniejsze do odczytania i zrozumienia, jeśli używasz dwóch useEffect
oddzielnie dla każdego ID zmiany chcesz je przetwarzać oddzielnie
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-23 12:22:38
Okaż, że ktoś szuka wersji maszynopisu usePrevious:
W module .tsx
:
import { useEffect, useRef } from "react";
const usePrevious = <T extends unknown>(value: T): T | undefined => {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
Lub w module .ts
:
import { useEffect, useRef } from "react";
const usePrevious = <T>(value: T): T | undefined => {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-19 22:49:40
Opcja 1-Uruchom useEffect po zmianie wartości
const Component = (props) => {
useEffect(() => {
console.log("val1 has changed");
}, [val1]);
return <div>...</div>;
};
Opcja 2-useHasChanged hook
Porównywanie bieżącej wartości do poprzedniej jest powszechnym wzorcem i uzasadnia własny hook, który ukrywa szczegóły implementacji.
const Component = (props) => {
const hasVal1Changed = useHasChanged(val1)
useEffect(() => {
if (hasVal1Changed ) {
console.log("val1 has changed");
}
});
return <div>...</div>;
};
const useHasChanged= (val: any) => {
const prevVal = usePrevious(val)
return prevVal !== val
}
const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-28 13:05:53
Wychodząc z zaakceptowanej odpowiedzi, alternatywnego rozwiązania, które nie wymaga niestandardowego Hooka:
const Component = ({ receiveAmount, sendAmount }) => {
const prevAmount = useRef({ receiveAmount, sendAmount }).current;
useEffect(() => {
if (prevAmount.receiveAmount !== receiveAmount) {
// process here
}
if (prevAmount.sendAmount !== sendAmount) {
// process here
}
return () => {
prevAmount.receiveAmount = receiveAmount;
prevAmount.sendAmount = sendAmount;
};
}, [receiveAmount, sendAmount]);
};
Zakłada to, że rzeczywiście potrzebujesz odniesienia do poprzednich wartości dla czegokolwiek w bitach" proces tutaj". W przeciwnym razie, o ile twoje uwarunkowania nie wykraczają poza proste !==
porównanie, najprostszym rozwiązaniem byłoby tutaj:
const Component = ({ receiveAmount, sendAmount }) => {
useEffect(() => {
// process here
}, [receiveAmount]);
useEffect(() => {
// process here
}, [sendAmount]);
};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-03 17:30:16
Właśnie opublikowałem react-delta który rozwiązuje dokładnie ten scenariusz. Moim zdaniem useEffect
ma zbyt wiele obowiązków.
Obowiązki
- porównuje wszystkie wartości w tablicy zależności za pomocą
Object.is
- uruchamia wywołania efektu / czyszczenia na podstawie wyniku # 1
Rozbijanie Obowiązków
react-delta
łamie obowiązki useEffect
na kilka mniejszych hooków.
Odpowiedzialność #1
usePrevious(value)
useLatest(value)
useDelta(value, options)
useDeltaArray(valueArray, options)
useDeltaObject(valueObject, options)
some(deltaArray)
every(deltaArray)
Odpowiedzialność #2
Z mojego doświadczenia wynika, że takie podejście jest bardziej elastyczne, czyste i zwięzłe niż useEffect
/useRef
rozwiązania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-12 15:31:48
Dla naprawdę prostego porównania prop możesz użyć useEffect
, aby łatwo sprawdzić, czy prop został zaktualizowany.
const myComponent = ({ prop }) => {
useEffect(() => {
---Do stuffhere----
}, [prop])
}
useEffect
uruchomi Kod tylko wtedy, gdy prop się zmieni.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-30 03:16:32
Ponieważ stan nie jest ściśle powiązany z instancją komponentu w komponentach funkcyjnych, poprzedni stan nie może zostać osiągnięty w useEffect
bez uprzedniego zapisania go, na przykład za pomocą useRef
. Oznacza to również, że aktualizacja stanu została prawdopodobnie nieprawidłowo zaimplementowana w niewłaściwym miejscu, ponieważ poprzedni stan jest dostępny wewnątrz setState
funkcji updater.
Jest to dobry przypadek użycia dla useReducer
, który zapewnia magazyn podobny do Redux i pozwala zaimplementować odpowiedni wzór. Aktualizacje stanu są wykonywane jawnie, więc nie ma potrzeby, aby dowiedzieć się, która własność państwa jest aktualizowana; jest to już jasne z wysłanej akcji.
Oto przykład Jak to może wyglądać:
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
});
...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-23 14:45:23
Użycie Ref wprowadzi nowy rodzaj błędu do aplikacji.
Zobaczmy ten przypadek używając usePrevious
, który ktoś skomentował wcześniej:
- prop.minTime: 5 = = > ref.current = 5 / set ref.current
- prop.minTime: 5 = = > ref.current = 5 / nowa wartość jest równa ref.current
- prop.minTime: 8 = = > ref.current = 5 / nowa wartość nie jest równa ref.current
- prop.minTime: 5 = = > ref.current = 5 / nowa wartość jest równa ref.current
Jak widzimy tutaj, my nie aktualizujemy wewnętrznego ref
, ponieważ używamy useEffect
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-28 15:32:23
Jeśli wolisz useEffect
podejście zastępcze:
const usePreviousEffect = (fn, inputs = []) => {
const previousInputsRef = useRef([...inputs])
useEffect(() => {
fn(previousInputsRef.current)
previousInputsRef.current = [...inputs]
}, inputs)
}
I użyj go tak:
usePreviousEffect(
([prevReceiveAmount, prevSendAmount]) => {
if (prevReceiveAmount !== receiveAmount) // side effect here
if (prevSendAmount !== sendAmount) // side effect here
},
[receiveAmount, sendAmount]
)
Zauważ, że pierwszy Czas wykonania efektu, poprzednie wartości przekazane do twojego fn
będą takie same jak początkowe wartości wejściowe. Ma to dla Ciebie znaczenie tylko wtedy, gdy chcesz coś zrobić, gdy wartość nie zmienia się .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-12 15:53:49
Oto Niestandardowy hook, którego używam, który moim zdaniem jest bardziej intuicyjny niż używanie usePrevious
.
import { useRef, useEffect } from 'react'
// useTransition :: Array a => (a -> Void, a) -> Void
// |_______| |
// | |
// callback deps
//
// The useTransition hook is similar to the useEffect hook. It requires
// a callback function and an array of dependencies. Unlike the useEffect
// hook, the callback function is only called when the dependencies change.
// Hence, it's not called when the component mounts because there is no change
// in the dependencies. The callback function is supplied the previous array of
// dependencies which it can use to perform transition-based effects.
const useTransition = (callback, deps) => {
const func = useRef(null)
useEffect(() => {
func.current = callback
}, [callback])
const args = useRef(null)
useEffect(() => {
if (args.current !== null) func.current(...args.current)
args.current = deps
}, deps)
}
Użyłbyś useTransition
w następujący sposób.
useTransition((prevRate, prevSendAmount, prevReceiveAmount) => {
if (sendAmount !== prevSendAmount || rate !== prevRate && sendAmount > 0) {
const newReceiveAmount = sendAmount * rate
// do something
} else {
const newSendAmount = receiveAmount / rate
// do something
}
}, [rate, sendAmount, receiveAmount])
Mam nadzieję, że to pomoże.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-08 13:22:19
Możesz użyć useImmer w przeciwieństwie do useState i uzyskać dostęp do stanu. Przykład: https://css-tricks.com/build-a-chat-app-using-react-hooks-in-100-lines-of-code/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-18 03:57:38
W Twoim przypadku (obiekt prosty):
useEffect(()=>{
// your logic
}, [rate, sendAmount, receiveAmount])
W innym przypadku (obiekt złożony)
const {cityInfo} = props;
useEffect(()=>{
// some logic
}, [cityInfo.cityId])
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-28 09:00:02