3. Testowanie
Jako programiści nie jesteśmy święci i często się mylimy, dlatego tutaj są spisane częste pomyłki przy pisaniu kodu oraz jak można nim zaradzić.
Używanie useEffect tam gdzie nie trzeba
useEffect służy do synchronizowania stanu z zewnętrznym systemem (ref. dokumentacja reacta), jeśli w useEffecie nie macie odwołania do zewnętrznego systemu (localStorage, api serwera, inne api przeglądarki) to znaczy, że go źle użyliście, przykładowy zły kod:
function handleMuscleClick(muscle: string) { setSelectedMuscles((prevSelectedMuscles) => { if (prevSelectedMuscles.includes(muscle)) { return prevSelectedMuscles.filter((m) => m !== muscle); } else { return [...prevSelectedMuscles, muscle]; } });}
useEffect(() => { table.getColumn("targetMuscle")?.setFilterValue(selectedMuscles);}, [selectedMuscles, table]);
Tutaj synchronizujemy selectedMuscles
z filtrowana wartościa z tabelce, tabelka żyje w świecie reacta i nie jest zewnętrznym systemem i równie dobrze możemy to zrobić w ten sposób:
function handleMuscleClick(muscle: string) { const newSelectedMuscles = selectedMuscles.includes(muscle) ? prevSelectedMuscles.filter((m) => m !== muscle) : [...prevSelectedMuscles, muscle];
table.getColumn("targetMuscle")?.setFilterValue(newSelectedMuscles); setSelectedMuscles(newSelectedMuscles);}
Teraz jest bardzo jasne, że po kliknięciu na mięsień, zostaje też zupdate’owana tabelka.
Ten przykład jeszcze nie jest taki zły, przy 1 useEffectcie dość łatwo się połapać co się dzieje, przy 11 robi się już kłopot i raz u mnie w pracy przez coś takiego padła produkcja 😭
Nieużywanie lub nadużywanie useMemo
Hook useMemo
pozwala na zapamiętanie wyniku funkcji obliczeniowej między renderami komponentu, co może pomóc w optymalizacji wydajności aplikacji. Jest używany głównie w przypadku “drogich” obliczeń (obliczenia, które swoją złożonością mają istotny wpływ na wydajność), które nie muszą być wykonywane na nowo przy każdym renderze.
Składnia:
const memoizedValue = useMemo(() => computeValue(a, b), [a, b]);
- computeValue: Funkcja zwracająca wynik obliczeń.
- [a, b]:
useMemo
ponownie obliczy wartość tylko wtedy, gdy jedna z zależności w tej tablicy się zmieni.
Kiedy używać useMemo
?
- Przy “kosztownych” obliczeniach, takich jak złożone przekształcenia dużych ilości danych.
- Kiedy wynik obliczeń jest wielokrotnie używany w danym renderze (np. w innych hookach).
- Przy obliczeniach, które zależą od dynamicznie zmieniających się danych.
Kiedy nie używać useMemo
?
- Jeśli obliczenia są szybkie i nie mają zauważalnego wpływu na wydajność.
- Gdy dodanie useMemo bardziej komplikuje kod niż przynosi korzyści.
- Gdy zależności często się zmieniają, co może prowadzić do częstych ponownych obliczeń.
Co to za błąd hydracji w React/Next.js?
Jeśli używaliście SSR (server-side-rendering) z hydracją po stronie klienta (czyli możliwość zachowania reaktywności strony przy jak największym renderowaniu po stronie serwera), to może napotkaliście się z błędem w stylu Hydration failed
:
Dzieje się to, gdy jakakolwiek część DOM-u ma inną wartość po stronie serwera niż po stronie klienta. Innymi słowy — wtedy, kiedy używacie kodu niedeterministycznego względem okresu, w jakim zachodzi hydracja.
Przykładami sytuacji, które mogłoby to wywołać to używanie dat, np przy countdownie lub używanie wartości losowo wygenerowanych.
Sugerowane podejścia
-
Renderowanie niedeterministycznych wartości wyłącznie po stronie klienta Zanim nastąpi hydracja, zwróć wersję komponentu bez treści dynamicznej. Gdy komponent zostanie w pełni załadowany, użyj
useEffect
aby zaktualizować komponent, co dzieje się po stronie klienta.function HydratedComponent() {const [isLoaded, setIsLoaded] = useState(false);const [currentDate, setCurrentDate] = useState(new Date());useEffect(() => {setIsLoaded(true);setCurrentDate(new Date());}, []);if (!isLoaded) return "Loading...";return currentDate.toLocaleDateString();} -
Tymczasowe zaokrąglanie wartości, aby w czasie hydracji nie zmienaiały się W przypadku dat, można użyć przybliżenia w takim stopniu, żeby w przewidywanym czasie załadowania strony nie uległo ono zmianie.
/** Rounds the current date to the nearest `accuracy` milliseconds. */const getRoundedDate = (accuracy = 60000) =>new Date(Math.round(new Date().getTime() / accuracy) * accuracy);function HydratedComponent() {const [currentDate, setCurrentDate] = useState(getRoundedDate());return currentDate.toLocaleString();}
Metoda #2 działa w przypadku wartości, które można w jakimś stopniu zaokrąglać lub czynić mniej dokładnymi. W związku z tym, dla wartości losowo wygenerowanych (np. Math.random()
), zaleca się stosowanie metody #1.