useReducer do złożonego stanu
Proszę modelować przejścia stanu funkcją reducera, wysyłać obiekty akcji oraz wybierać useReducer zamiast useState, gdy logika stanu staje się złożona.
useReducer do złożonego stanu to bezpłatna lekcja Frontend Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Frontend Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Frontend Academy zawiera 4 lekcji w sumie.
Kiedy useState to za mało
Gdy przejścia stanu obejmują złożoną logikę, wiele powiązanych wartości lub sytuacje, w których nowy stan zależy od poprzedniego w nietrywialny sposób, useReducer jest lepszym wyborem niż wiele wywołań useState.
Wzorzec funkcji reducer
Reducer jest funkcją czystą: (state, action) => newState. Przyjmuje bieżący stan i obiekt akcji, a następnie zwraca kolejny stan. Nie należy mutować stanu — zawsze należy zwracać nowy obiekt.
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset'; payload: number };
function counterReducer(state: number, action: Action): number {
switch (action.type) {
case 'increment': return state + 1;
case 'decrement': return state - 1;
case 'reset': return action.payload;
default: return state;
}
}Składnia useReducer
useReducer(reducer, initialState) zwraca bieżący stan oraz funkcję dispatch. Należy wywołać dispatch z obiektem akcji, aby wywołać przejście stanu.
function Counter() {
const [count, dispatch] = useReducer(counterReducer, 0);
return (
<div>
<p>{count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
</div>
);
}Typowane akcje — unie dyskryminowane
Należy używać w TypeScript typów unii dyskryminowanej dla akcji, aby uzyskać wyczerpujące sprawdzanie typów w instrukcji switch reducera.
type CartAction =
| { type: 'ADD_ITEM'; item: CartItem }
| { type: 'REMOVE_ITEM'; id: string }
| { type: 'SET_QUANTITY'; id: string; qty: number }
| { type: 'CLEAR' };Przykład złożonego stanu: koszyk zakupowy
Koszyk zakupowy z operacjami dodawania, usuwania i aktualizowania ilości to idealny przypadek użycia useReducer.
interface CartState {
items: CartItem[];
total: number;
}
function cartReducer(state: CartState, action: CartAction): CartState {
switch (action.type) {
case 'ADD_ITEM': {
const exists = state.items.find(i => i.id === action.item.id);
if (exists) {
const items = state.items.map(i =>
i.id === action.item.id ? { ...i, qty: i.qty + 1 } : i
);
return { ...state, items, total: calcTotal(items) };
}
const items = [...state.items, { ...action.item, qty: 1 }];
return { ...state, items, total: calcTotal(items) };
}
case 'CLEAR': return { items: [], total: 0 };
default: return state;
}
}Funkcja dispatch jest stabilna
Funkcja dispatch jest stabilna — nie zmienia się między renderowaniami. Można ją bezpiecznie przekazywać jako props lub używać jako zależności useEffect bez wywoływania ponownych uruchomień.
Łączenie useReducer i useContext
To potężny wzorzec: kontekst udostępnia zarówno stan, jak i dispatch, dzięki czemu dowolny komponent korzystający z kontekstu może odczytywać stan lub wysyłać akcje bez prop drillingu.
const CartContext = createContext<{ state: CartState; dispatch: Dispatch<CartAction> } | null>(null);
function CartProvider({ children }: { children: React.ReactNode }) {
const [state, dispatch] = useReducer(cartReducer, { items: [], total: 0 });
return <CartContext.Provider value={{ state, dispatch }}>{children}</CartContext.Provider>;
}useReducer a useState
Należy wybrać useReducer, gdy: stan ma wiele podwartości, kolejny stan zależy od poprzedniego w złożony sposób, przejść jest wiele i mają one jawnie nazwane akcje albo logika stanu ma być testowana niezależnie od komponentu.
Testowanie reducerów w izolacji
Ponieważ reducery są funkcjami czystymi, można je z łatwością testować bez renderowania jakiegokolwiek komponentu.
test('increment action increases count', () => {
expect(counterReducer(0, { type: 'increment' })).toBe(1);
expect(counterReducer(5, { type: 'increment' })).toBe(6);
});
test('reset action sets count to payload', () => {
expect(counterReducer(42, { type: 'reset', payload: 0 })).toBe(0);
});Immer do niemutowalnych aktualizacji
Biblioteka immer pozwala pisać kod wyglądający na mutujący, który w rzeczywistości tworzy nowy, niemutowalny stan. Jest przydatna przy aktualizowaniu złożonych zagnieżdżonych obiektów w reducerach.
Szybki test
Jaki jest podpis funkcji reducera?
Podsumowanie: useReducer
(state, action) => newState — funkcja czysta, bez mutacji. useReducer(reducer, initial) zwraca [state, dispatch]. Dla bezpieczeństwa należy używać typów unii dyskryminowanej dla akcji. dispatch jest stabilne. useReducer świetnie sprawdza się w przypadku złożonego stanu z wieloma przejściami. Można łączyć go z useContext do zarządzania stanem globalnym. Reducery należy testować niezależnie jako funkcje czyste.
Często zadawane pytania
Czy lekcja „useReducer do złożonego stanu” jest bezpłatna?
Tak — pełny tekst „useReducer do złożonego stanu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Frontend Academy, przejdź na CoddyKit PRO. Kurs Frontend Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „useReducer do złożonego stanu”?
Proszę modelować przejścia stanu funkcją reducera, wysyłać obiekty akcji oraz wybierać useReducer zamiast useState, gdy logika stanu staje się złożona. Ćwiczysz Frontend Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Frontend Academy?
Nie wymagamy żadnego doświadczenia. Frontend Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „useReducer do złożonego stanu”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Frontend Academy?
Tak. Każda lekcja Frontend Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- useContext do stanu globalnego
- useReducer do złożonego stanu
- useMemo i useCallback na potrzeby wydajności
- Własne hooki: wydzielanie wielokrotnego użytku logiki