Reducer pattern 🔄
Reducer pattern je pokročilejší způsob správy stavu v Reactu, který je ideální pro komplexní logiku a složité stavy. Je to stejný koncept jako v Reduxu, ale vestavěný do Reactu.
🎯 Co je Reducer pattern?
Reducer je čistá funkce, která bere současný stav a akci, a vrací nový stav. Je to předvídatelný způsob, jak spravovat složité stavy.
Kdy použít useReducer:
✅ Složitý stav - více hodnot, které se mění společně
✅ Komplexní logika aplikace - složitější výpočty a transformace
✅ Předvídatelnost - stejný vstup = stejný výstup
✅ Testovatelnost - čisté funkce jsou snadno testovatelné
✅ Time travel - možnost návratu k předchozím stavům
Kdy NEPOUŽÍVAT useReducer:
❌ Jednoduchý stav - můžeš použít useState
❌ Primitivní hodnoty - string, number, boolean (žádné objekty, pole, funkce atd.)
❌ Nezávislé hodnoty - hodnoty, které se nemění společně (např. hodnoty, které se mění nezávisle na sobě)
🚀 Základní použití
1. Definice akcí:
Akce jsou textové konstanty, které popisují, co se stalo a iniciují akci v reduceru.
Tyto konstanty jsou použity v reduceru pro spuštění příslušné logiky.
// actions/authActions.js
export const AUTH_ACTIONS = {
LOGIN: 'LOGIN',
LOGOUT: 'LOGOUT',
};
// Action creators
export const login = (user) => ({
type: AUTH_ACTIONS.LOGIN,
payload: user
});
export const logout = () => ({
type: AUTH_ACTIONS.LOGOUT
});
2. Reducer funkce:
⚠️ Už dříve jsme si říkali, že když měníme stav, musíme vytvořit nový objekt pro React, aby re-renderoval komponentu.
Všimněte si, že v reduceru používáme destrukturování objektu
statepomocí rest operatoru...state.Tímto způsobem získáme všechny vlastnosti objektu
statea přidáme k nim nové vlastnosti, které jsou v action payloadu (v nových datech).
// reducers/authReducer.js
import { AUTH_ACTIONS } from '../actions/authActions';
const initialState = {
user: null,
isAuthenticated: false,
};
export const authReducer = (state, action) => {
switch (action.type) {
case AUTH_ACTIONS.LOGIN:
return {
...state,
user: action.payload,
isAuthenticated: true,
};
case AUTH_ACTIONS.LOGOUT:
return {
...state,
user: null,
isAuthenticated: false,
};
default:
return state;
}
};
3. Použití v komponentě:
// contexts/AuthContext.jsx
import React, { createContext, useContext, useReducer } from 'react';
import { authReducer, initialState } from '../reducers/authReducer';
import { login, logout } from '../actions/authActions';
import apiService from '../services/api';
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, initialState);
// Async actions
const handleLogin = async (email, password) => {
try {
const user = await apiService.login(email, password);
dispatch(login(user));
} catch (error) {
throw error;
}
};
const handleLogout = () => {
dispatch(logout());
};
// Context value
const value = {
// State
user: state.user,
isAuthenticated: state.isAuthenticated,
// Actions
login: handleLogin,
logout: handleLogout,
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth musí být použit uvnitř AuthProvider');
}
return context;
};
🎨 Pokročilé funkce
1. Reducer s TypeScript:
// types/auth.ts
export interface User {
id: number;
email: string;
name: string;
}
export interface AuthState {
user: User | null;
isAuthenticated: boolean;
}
export interface AuthAction {
type: string;
payload?: User;
}
// reducers/authReducer.ts
import { AuthState, AuthAction } from '../types/auth';
import { AUTH_ACTIONS } from '../actions/authActions';
const initialState: AuthState = {
user: null,
isAuthenticated: false,
};
export const authReducer = (
state: AuthState = initialState,
action: AuthAction
): AuthState => {
switch (action.type) {
case AUTH_ACTIONS.LOGIN:
return {
...state,
user: action.payload || null,
isAuthenticated: true,
};
case AUTH_ACTIONS.LOGOUT:
return {
...state,
user: null,
isAuthenticated: false,
};
default:
return state;
}
};
⚡ Performance optimalizace
Memoization:
// contexts/AuthContext.jsx
import { useMemo, useCallback } from 'react';
export const AuthProvider = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, initialState);
const handleLogin = useCallback(async (email, password) => {
try {
const user = await apiService.login(email, password);
dispatch(login(user));
} catch (error) {
throw error;
}
}, []);
const handleLogout = useCallback(() => {
dispatch(logout());
}, []);
// Memoizace hodnoty contextu
const value = useMemo(() => ({
user: state.user,
isAuthenticated: state.isAuthenticated,
login: handleLogin,
logout: handleLogout,
}), [state.user, state.isAuthenticated, handleLogin, handleLogout]);
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
};
📋 Praktické cvičení
- Implementujte authReducer s akcemi LOGIN a LOGOUT
- Vytvořte action creators pro login a logout
- Přidejte TypeScript typy pro lepší type safety
- Implementujte async akci pro přihlášení přes API
- Otestujte reducer pomocí unit testů
🚀 Tipy a triky
1. Immutable updates:
V Reactu musíme vždy vytvářet nový objekt při změně stavu, abychom re-renderovali komponentu. To platí i pro reducery.
// ✅ Správně - immutable update - vytvoříme nový objekt ze stávajících dat a přidáme nové vlastnosti.
case AUTH_ACTIONS.LOGIN:
return {
...state,
user: action.payload,
isAuthenticated: true,
};
// ❌ Špatně - mutable update - měníme stav přímo, tak se komponenta ne-renderuje.
case AUTH_ACTIONS.LOGIN:
state.user = action.payload;
state.isAuthenticated = true;
return state;
2. Debugging:
// Přidejte logging pro debugging
export const authReducer = (state, action) => {
console.log('Action:', action.type, action.payload);
console.log('Previous state:', state);
const newState = /* reducer logic */;
console.log('New state:', newState);
return newState;
};
Reducer pattern je výkonný nástroj pro komplexní správu stavu. V příští části se podíváme na moderní state management knihovny! 🚀