Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Przechowywanie wartości między renderowaniami z useRef

W kosmicznej podróży przez React, czasami potrzebujemy przechowywać informacje, które przetrwają między renderowaniami komponentu, ale nie powinny powodować ponownego renderowania. Hook useRef jest jak kapsuła ratunkowa - przechowuje dane, które możesz odzyskać w dowolnym momencie, bez względu na to, ile razy twój statek kosmiczny (komponent) zostanie przebudowany.

Czym jest useRef?

Hook

useRef
tworzy mutowalny obiekt referencyjny, którego właściwość
.current
jest inicjalizowana przekazaną wartością. Ważną cechą useRef jest to, że:

  1. Wartość przechowywana w referencji pozostaje taka sama między renderowaniami
  2. Zmiana wartości referencji NIE powoduje ponownego renderowania komponentu
  3. Działa jak "magazyn pamięci", do którego możesz się odwoływać w dowolnym momencie w cyklu życia komponentu

useRef vs useState - kluczowe różnice

Porównajmy dwie kluczowe metody przechowywania danych w komponentach:

1function SpaceshipDashboard() {
2  // Stan - zmiany powodują ponowne renderowanie
3  const [speed, setSpeed] = useState(0);
4
5  // Referencja - zmiany NIE powodują ponownego renderowania
6  const previousSpeedRef = useRef(0);
7
8  // Przy zmianie prędkości, zapisujemy poprzednią wartość
9  useEffect(() => {
10    previousSpeedRef.current = speed;
11  }, [speed]);
12
13  const increaseSpeed = () => {
14    setSpeed(speed + 10); // To spowoduje ponowne renderowanie
15  };
16
17  return (
18    <div>
19      <p>Aktualna prędkość: {speed} km/h</p>
20      <p>Poprzednia prędkość: {previousSpeedRef.current} km/h</p>
21      <button onClick={increaseSpeed}>Zwiększ prędkość</button>
22    </div>
23  );
24}

Typowe przypadki użycia useRef

1. Przechowywanie poprzednich wartości

Jednym z najpopularniejszych zastosowań useRef jest zapamiętywanie poprzednich wartości między renderowaniami:

1function AsteroidTracker({ asteroid }) {
2  const [position, setPosition] = useState({ x: 0, y: 0 });
3  const previousPositionRef = useRef({ x: 0, y: 0 });
4
5  useEffect(() => {
6    // Zapisujemy aktualną pozycję jako poprzednią przed następną aktualizacją
7    previousPositionRef.current = position;
8  }, [position]);
9
10  // Obliczamy, jak daleko poruszył się asteroid
11  const distanceMoved = Math.sqrt(
12    Math.pow(position.x - previousPositionRef.current.x, 2) +
13    Math.pow(position.y - previousPositionRef.current.y, 2)
14  );
15
16  return (
17    <div>
18      <p>Aktualna pozycja: ({position.x}, {position.y})</p>
19      <p>Poprzednia pozycja: ({previousPositionRef.current.x}, {previousPositionRef.current.y})</p>
20      <p>Przebyta odległość: {distanceMoved}</p>
21    </div>
22  );
23}

2. Liczniki i wartości instancji, które nie wpływają na renderowanie

1function MissionControl() {
2  const missionTimeRef = useRef(0);
3  const timerIdRef = useRef(null);
4
5  // Rozpoczęcie licznika misji
6  const startMissionTimer = () => {
7    if (timerIdRef.current !== null) return; // Unikamy wielokrotnego uruchamiania
8
9    timerIdRef.current = setInterval(() => {
10      missionTimeRef.current += 1;
11      console.log(\`Czas misji: \${missionTimeRef.current} sekund\`);
12      // Zauważ, że to NIE powoduje ponownego renderowania komponentu
13    }, 1000);
14  };
15
16  // Zatrzymanie licznika misji
17  const stopMissionTimer = () => {
18    clearInterval(timerIdRef.current);
19    timerIdRef.current = null;
20  };
21
22  // Czyszczenie przy odmontowaniu komponentu
23  useEffect(() => {
24    return () => {
25      if (timerIdRef.current !== null) {
26        clearInterval(timerIdRef.current);
27      }
28    };
29  }, []);
30
31  return (
32    <div>
33      <button onClick={startMissionTimer}>Rozpocznij misję</button>
34      <button onClick={stopMissionTimer}>Zakończ misję</button>
35      <button onClick={() => alert(\`Czas misji: \${missionTimeRef.current} sekund\`)}>
36        Pokaż czas misji
37      </button>
38    </div>
39  );
40}

3. Dostęp do elementów DOM

1function SpaceshipScanner() {
2  const scannerInputRef = useRef(null);
3
4  const focusScannerInput = () => {
5    // Bezpośredni dostęp do elementu DOM
6    scannerInputRef.current.focus();
7  };
8
9  return (
10    <div>
11      <input
12        ref={scannerInputRef}
13        type="text"
14        placeholder="Wprowadź współrzędne..."
15      />
16      <button onClick={focusScannerInput}>Aktywuj skaner</button>
17    </div>
18  );
19}

Pamiętaj o zasadach używania useRef

  1. Nie nadużywaj go: Używaj useRef tylko wtedy, gdy wartość naprawdę nie powinna powodować ponownego renderowania
  2. Unikaj bezpośrednich mutacji DOM: Chociaż useRef daje ci dostęp do elementów DOM, używaj go oszczędnie i zgodnie z filozofią Reacta
  3. Pamiętaj o czyszczeniu: Jeśli używasz useRef do przechowywania timerów lub subskrypcji, pamiętaj o ich czyszczeniu w funkcji czyszczącej useEffect

Podsumowanie

Hook useRef jest nieoceniony, gdy potrzebujesz:

  • Przechować wartość między renderowaniami bez powodowania ponownego renderowania
  • Śledzić poprzednie wartości
  • Uzyskać bezpośredni dostęp do elementów DOM
  • Przechowywać identyfikatory timerów lub inne wartości instancji

Jak kapsuła ratunkowa w kosmosie, useRef przechowuje cenne dane, które mogą być potrzebne w krytycznych momentach misji twojego komponentu.

Vai a CodeWorlds