React od wersji 16.8 posiada Hooki, które umożliwiają zarządzanie stanem oraz wykonywanie tzw. efektów ubocznych w komponentach funkcyjnych.
Jednym z najbardziej podstawowych i powszechnie używanych hooków jest useState.
W tym artykule wyjaśnię Ci:
- czym jest useState,
- jak działa
- jak można go używać w różnych sytuacjach
- dobre praktyki w kwestii użycia useState()
- najczęściej popełniane błędy
- przykładowe zadania rekrutacyjne z zastosowaniem hooka useState() .
Co to są Hooki w Reakcie?
Hooki to specjalne funkcje w React, które pozwalają na "zaczepienie" się w stan i cykl życia komponentów funkcyjnych.
Wprowadzone w Reakcie 16.8, hooki przekształciły sposób, w jaki twórcy aplikacji pracują z komponentami funkcyjnymi, umożliwiając im wykorzystanie funkcji, które wcześniej były dostępne tylko w komponentach klasowych.
🚀 Dlaczego Hooki to obecnie fundament Reacta?
- Prostota: Hooki umożliwiają użycie stanu i innych funkcji Reacta bez potrzeby tworzenia klas. Dzięki nim komponenty funkcyjne mogą być bardziej czytelne i prostsze w zarządzaniu.
- Reużywalność: Możesz tworzyć własne hooki (custom hooks), co pozwala na reużywanie logiki między różnymi komponentami.
- Elastyczność: Hooki dają większą kontrolę nad stanem i efektami w komponentach, co czyni je bardziej elastycznymi i łatwiejszymi w użyciu.
Dlaczego potrzebujemy Hooków?
Przed wprowadzeniem hooków, zarządzanie stanem w Reactie było ograniczone do komponentów klasowych. Hooki, takie jak useState, pozwalają na:
- Zarządzanie stanem bez potrzeby tworzenia komponentów klasowych.
- Unikanie złożoności związanej z metodami cyklu życia komponentów.
- Ułatwienie współdzielenia logiki między różnymi komponentami.
Co to jest useState?
useState to hook, który umożliwia dodanie stanu do komponentu funkcyjnego. Używając useState, możemy zarządzać zmiennymi stanu w komponentach, które wcześniej musiałyby być komponentami klasowymi.
Jak działa useState?
Deklaracja useState wygląda następująco:
const [state, setState] = useState(initialState);
- state: Aktualna wartość stanu.
- setState: Funkcja służąca do aktualizacji wartości stanu.
- initialState: Wartość początkowa stanu.
useState zwraca tablicę zawierającą dwa elementy: aktualny stan oraz funkcję do jego aktualizacji. Dzięki destrukturyzacji tablicy możemy nadać tym elementom dowolne nazwy.
Podstawowy Przykład użycia useState
Rozważmy prosty przykład komponentu, który wyświetla licznik i przycisk, który zwiększa wartość licznika o 1 za każdym razem, gdy zostanie kliknięty.
Przykład bez użycia useState:
import React from "react";
import ReactDOM from "react-dom";
let count = 0;
function increase() {
count++;
ReactDOM.render(
<div className="container">
<h1>{count}</h1>
<button onClick={increase}>+</button>
</div>,
document.getElementById("root")
);
}
ReactDOM.render(
<div className="container">
<h1>{count}</h1>
<button onClick={increase}>+</button>
</div>,
document.getElementById("root")
);
W powyższym kodzie za każdym razem, gdy przycisk jest klikany, musimy ręcznie renderować komponent, aby zaktualizować wyświetlaną wartość licznika. Jest to nieefektywne i niezgodne z filozofią Reacta.
Przykład z użyciem useState:
import React, { useState } from "react";
function App() {
const [count, setCount] = useState(0);
function increase() {
setCount(count + 1);
}
return (
<div className="container">
<h1>{count}</h1>
<button onClick={increase}>+</button>
</div>
);
}
Dzięki użyciu useState, możemy łatwo zarządzać stanem licznika i automatycznie aktualizować interfejs użytkownika po każdej zmianie stanu.
Głębsze Zrozumienie useState
Inicjalizacja Stanu
Stan zadeklarowany przez useState może być inicjalizowany nie tylko wartościami prymitywnymi, ale również obiektami czy funkcjami.
const [user, setUser] = useState({ name: "John", age: 30 });
Możemy również przekazać funkcję jako argument do useState, aby obliczyć stan początkowy tylko raz, podczas pierwszego renderowania komponentu.
const [count, setCount] = useState(() => computeExpensiveInitialState());
Aktualizacja Stanu
Funkcja setState
może być użyta do aktualizacji stanu. Istnieją dwa główne sposoby aktualizacji stanu:
-
Bezpośrednie ustawienie nowej wartości stanu:
javascriptsetCount(count + 1);
-
Funkcja aktualizująca (gdy nowa wartość stanu zależy od poprzedniego stanu):
javascriptsetCount((prevCount) => prevCount + 1);
Zarządzanie Złożonym Stanem
Jeśli stan jest obiektem z wieloma właściwościami, musimy być ostrożni podczas aktualizacji, aby nie nadpisać całego obiektu.
const [state, setState] = useState({ name: "John", age: 30 });
function updateName(newName) {
setState((prevState) => ({
...prevState,
name: newName,
}));
}
Zaawansowane Przykłady użycia useState
Przełącznik Światła
Stwórzmy przykład przełącznika światła, który zmienia kolor tła między białym a czarnym.
import React, { useState } from "react";
function LightSwitch() {
const [isOn, setIsOn] = useState(false);
function toggleLight() {
setIsOn(!isOn);
}
return (
<div
style={{
height: "100vh",
background: isOn ? "white" : "black",
color: isOn ? "black" : "white",
}}
onClick={toggleLight}
>
{isOn ? "Light is On" : "Light is Off"}
</div>
);
}
Formularz Logowania
Przykład formularza logowania, który wykorzystuje useState do zarządzania danymi wejściowymi.
import React, { useState } from "react";
function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
function handleSubmit(event) {
event.preventDefault();
alert(`Email: ${email}, Password: ${password}`);
}
return (
<form onSubmit={handleSubmit}>
<div>
<label>Email:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label>Password:</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<button type="submit">Login</button>
</form>
);
}
Zegar z Aktualnym Czasem
Stwórzmy zegar, który pokazuje aktualny czas i aktualizuje się co sekundę.
import React, { useState, useEffect } from "react";
function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(timer); // Wyczyść setInterval podczas odmontowania komponentu
}, []);
return <h1>{time.toLocaleTimeString()}</h1>;
}
Optymalizacja i Najlepsze Praktyki
Unikanie Niepotrzebnych Renderów
Używając setState
, warto unikać niepotrzebnych renderów komponentów. Należy zadbać o to, aby aktualizować stan tylko wtedy, gdy jest to konieczne.
function increment() {
if (count < 10) {
setCount(count + 1);
}
}
Łączenie Stanów
Chociaż można używać wielu useState
do zarządzania różnymi częściami stanu, warto zastanowić się nad łączeniem ich w jeden bardziej złożony obiekt, jeśli te stany są ściśle powiązane.
const [formState, setFormState] = useState({
name: "",
email: "",
password: "",
});
function updateFormState(field, value) {
setFormState({
...formState,
[field]: value,
});
}
Debugowanie
Używanie hooków takich jak useState może sprawić, że debugowanie stanu stanie się bardziej skomplikowane. Warto używać narzędzi takich jak React Developer Tools, które ułatwiają śledzenie stanu komponentów.
Najczęściej popełniane błędy przy używaniu useState
1. Aktualizacja stanu bez funkcji aktualizującej
Jednym z częstych błędów jest próba bezpośredniej aktualizacji stanu zamiast użycia funkcji setState
.
Błąd:
const [count, setCount] = useState(0);
function wrongIncrement() {
count++; // Błąd: bezpośrednia modyfikacja stanu
}
Poprawnie:
function correctIncrement() {
setCount(count + 1);
}
2. Brak inicjalizacji stanu
Inicjalizowanie stanu w useState jest kluczowe. Nie można wywołać useState
bez podania wartości początkowej.
Błąd:
const [count, setCount] = useState(); // Błąd: brak wartości początkowej
Poprawnie:
const [count, setCount] = useState(0); // Poprawne: wartość początkowa jest ustawiona
3. Ignorowanie poprzedniego stanu przy aktualizacji
Jeśli nowy stan zależy od poprzedniego, należy użyć funkcji aktualizującej, aby uniknąć błędów.
Błąd:
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1); // Może spowodować błędy w zależności od renderowania
}
Poprawnie:
function safeIncrement() {
setCount((prevCount) => prevCount + 1); // Bezpieczne użycie poprzedniego stanu
}
4. Używanie useState poza komponentem funkcyjnym
Hooki muszą być używane wewnątrz komponentów funkcyjnych lub innych hooków, nie można ich wywoływać w zwykłych funkcjach lub poza komponentami.
Błąd:
function useStateOutsideComponent() {
const [state, setState] = useState(0); // Błąd: użycie poza komponentem
}
Poprawnie:
function MyComponent() {
const [state, setState] = useState(0); // Poprawne: użycie wewnątrz komponentu funkcyjnego
return <div>{state}</div>;
}
5. Zapomnienie o funkcji czyszczącej po efektach
Przy używaniu useState
w połączeniu z useEffect
, ważne jest, aby zadbać o czyszczenie zasobów, aby uniknąć wycieków pamięci.
Błąd:
useEffect(() => {
const interval = setInterval(() => {
setCount((prevCount) => prevCount + 1);
}, 1000);
// Brak funkcji czyszczącej
}, []);
Poprawnie:
useEffect(() => {
const interval = setInterval(() => {
setCount((prevCount) => prevCount + 1);
}, 1000);
return () => clearInterval(interval); // Funkcja czyszcząca
}, []);
Dobre praktyki przy używaniu useState
1. Grupa powiązanych stanów w jednym obiekcie
Jeśli masz kilka powiązanych wartości stanu, rozważ przechowywanie ich w jednym obiekcie, aby ułatwić zarządzanie.
const [user, setUser] = useState({ name: "", age: 0 });
function updateUser(field, value) {
setUser((prevUser) => ({
...prevUser,
[field]: value,
}));
}
2. Rozdzielenie logiki aktualizacji od komponentu
Aby zachować czystość komponentu, logika aktualizacji stanu może być przeniesiona do zewnętrznych funkcji lub hooków.
function useCounter(initialValue) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
return { count, increment, decrement };
}
function CounterComponent() {
const { count, increment, decrement } = useCounter(0);
return (
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
);
}
3. Używanie wbudowanych typów Hooków
React Developer Tools oferują wsparcie dla wbudowanych hooków, co ułatwia debugowanie i śledzenie stanu. Używaj wbudowanych hooków zamiast tworzyć własne, jeśli jest to możliwe.
4. Unikanie złożonego stanu w jednym hooku
Jeśli stan jest zbyt złożony, rozważ podzielenie go na kilka useState
lub rozważ użycie useReducer
dla bardziej złożonej logiki aktualizacji stanu.
const [name, setName] = useState("");
const [age, setAge] = useState(0);
5. Optymalizacja użycia pamięci
Jeśli stan początkowy jest wynikiem złożonego obliczenia, przekaż funkcję do useState
, aby obliczyć go tylko raz, przy pierwszym renderze.
const [expensiveState, setExpensiveState] = useState(() =>
computeExpensiveState()
);
Podchwytliwe sytuacje z useState
1. Asynchroniczne aktualizacje
Stany w React są aktualizowane asynchronicznie, co może prowadzić do podchwytliwych sytuacji, jeśli próbujesz używać aktualnej wartości stanu zaraz po jej ustawieniu.
Błąd:
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count); // Może nie wyświetlić zaktualizowanej wartości
}
Poprawnie:
useEffect(() => {
console.log(count); // Zawsze pokaże aktualną wartość
}, [count]);
2. Efekty uboczne w funkcji renderującej
Nie umieszczaj funkcji setState
lub innych efektów ubocznych bezpośrednio w funkcji renderującej, ponieważ może to prowadzić do nieskończonych pętli renderowania.
Błąd:
const [count, setCount] = useState(0);
function App() {
setCount(count + 1); // Błąd: powoduje nieskończoną pętlę renderowania
return <h1>{count}</h1>;
}
Poprawnie:
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1);
}, []);
return <h1>{count}</h1>;
}
Podsumowanie
useState jest jednym z najważniejszych narzędzi w Reakcie, które umożliwia dodanie stanu do komponentów funkcyjnych. Dzięki useState możemy zarządzać i aktualizować stan aplikacji w sposób efektywny i zrozumiały.
Od prostych liczników po złożone formularze i dynamiczne interfejsy, useState dostarcza elastyczność potrzebną do budowy interaktywnych aplikacji.
Hooki, takie jak useState, zmieniają sposób, w jaki programujemy w Reakcie, umożliwiając nam tworzenie bardziej modularnych i łatwiejszych do zrozumienia komponentów.
Jeśli chcesz dowiedzieć się więcej o hookach, sprawdź również inne popularne hooki, takie jak useEffect i useContext.
Mam nadzieję, że ten artykuł pomógł Ci zrozumieć, czym jest useState i jak można go efektywnie wykorzystać w Twoich projektach Reactowych!