Pytania rekrutacyjne Zustand 2026: Zarządzanie stanem w React i najlepsze praktyki
Przygotuj się do rozmowy kwalifikacyjnej z pytań o Zustand w 2026 roku. Poznaj zarządzanie stanem w React, porównanie z Context API, middleware oraz wzorce TypeScript.

Zustand stał się preferowaną biblioteką do zarządzania stanem w aplikacjach React, które wymagają więcej niż Context API, ale mniej ceremonii niż Redux. Dzięki minimalistycznemu API i projektowi zorientowanemu na TypeScript, pytania o Zustand regularnie pojawiają się na rozmowach kwalifikacyjnych dla frontend developerów, szczególnie na stanowiskach mid-level i senior.
Pytania rekrutacyjne o Zustand testują trzy obszary: zrozumienie wzorca store vs wbudowany stan React, wiedzę o tym kiedy Zustand przewyższa alternatywy oraz umiejętność strukturyzacji stores pod kątem utrzymywalności.
Podstawowe koncepcje Zustand, które powinien znać każdy kandydat
Zustand v5, wydany pod koniec 2024 roku, wprowadził kilka przełomowych zmian w stosunku do v4. Funkcja create nie wymaga już oddzielnego wywołania useStore, a middleware persist domyślnie używa synchronicznego modelu hydratacji. Rekruterzy oczekują, że kandydaci znają aktualne API.
Co odróżnia Zustand od useState i useReducer?
Store Zustand istnieją poza drzewem komponentów React. Ta architektoniczna różnica ma trzy praktyczne konsekwencje:
- Stan persystuje między montowaniem i odmontowywaniem komponentów bez potrzeby Context providerów
- Aktualizacje nie wywołują re-renderów komponentów nadrzędnych, tylko subskrybowane komponenty są ponownie renderowane
- Stan może być dostępny synchronicznie poza React, co jest przydatne dla event handlerów i logiki asynchronicznej
import { create } from 'zustand'
interface CartStore {
items: CartItem[]
addItem: (item: CartItem) => void
clearCart: () => void
totalPrice: () => number
}
export const useCartStore = create<CartStore>((set, get) => ({
items: [],
addItem: (item) => set((state) => ({
items: [...state.items, item]
})),
clearCart: () => set({ items: [] }),
totalPrice: () => get().items.reduce((sum, item) => sum + item.price, 0)
}))Funkcja get zapewnia synchroniczny dostęp do aktualnego stanu, unikając problemu stale closure typowego dla useState.
Jak Zustand obsługuje re-rendery?
Zustand domyślnie używa płytkiego porównania (shallow equality). Gdy komponent subskrybuje store za pomocą useCartStore(), otrzymuje cały obiekt stanu i re-renderuje się przy każdej zmianie. Wybieranie konkretnych fragmentów zapobiega niepotrzebnym re-renderom:
// Źle: re-renderuje się przy każdej zmianie store
const { items } = useCartStore()
// Dobrze: re-renderuje się tylko gdy items się zmienią
const items = useCartStore((state) => state.items)
// Wiele wartości: użyj shallow comparison
import { shallow } from 'zustand/shallow'
const { items, totalPrice } = useCartStore(
(state) => ({ items: state.items, totalPrice: state.totalPrice() }),
shallow
)Komparator shallow z zustand/shallow wykonuje porównanie referencji na właściwościach obiektu, a nie na samym obiekcie.
Zustand vs Context API: Kiedy wybrać które rozwiązanie
To pytanie porównawcze pojawia się na niemal każdej rozmowie kwalifikacyjnej dotyczącej zarządzania stanem. Odpowiedź zależy od częstotliwości aktualizacji i złożoności stanu.
| Kryterium | Context API | Zustand |
|---|---|---|
| Rozmiar bundle | 0 KB (wbudowane) | 1.2 KB gzipped |
| Kontrola re-renderów | Ręczna z memo/useMemo | Wbudowane selektory |
| DevTools | Tylko React DevTools | Kompatybilne z Redux DevTools |
| Server components | Pełne wsparcie | Wymaga granicy klienta |
| Stan asynchroniczny | Wymaga wrappera | Natywne wsparcie |
Context API sprawdza się dobrze dla wartości zmieniających się rzadko, jak motyw czy locale. Zustand przewyższa w sytuacjach, gdy aktualizacje stanu są częste, na przykład inputy formularzy, dane real-time czy koszyki zakupowe.
Store Zustand wymagają dyrektywy "use client". Dla aplikacji intensywnie wykorzystujących React Server Components oznacza to, że stan Zustand żyje tylko w komponentach klienckich. Hybrydowe architektury często przekazują dane pobrane z serwera jako props do komponentów klienckich, które następnie synchronizują się z Zustand.
Wzorce middleware w Zustand
Pytania o middleware testują, czy kandydat rozumie kompozycję ponad konfiguracją. Middleware Zustand opakowuje funkcję tworzącą store, dodając zachowanie bez modyfikowania podstawowego API.
Jak działa middleware persist?
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
interface UserPreferences {
theme: 'light' | 'dark'
language: string
setTheme: (theme: 'light' | 'dark') => void
}
export const usePreferencesStore = create<UserPreferences>()(
persist(
(set) => ({
theme: 'light',
language: 'en',
setTheme: (theme) => set({ theme })
}),
{
name: 'user-preferences',
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({ theme: state.theme, language: state.language })
}
)
)Opcja partialize wyklucza funkcje i stan pochodny z persystencji. Bez niej serializacja zawodzi lub storage puchnie z niepotrzebnymi danymi.
Łączenie wielu middleware
Middleware komponuje się od wewnątrz na zewnątrz. Najbardziej wewnętrzne middleware wykonuje się jako pierwsze:
import { create } from 'zustand'
import { devtools, persist, subscribeWithSelector } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
export const useStore = create<StoreState>()(
devtools(
persist(
subscribeWithSelector(
immer((set) => ({
// stan i akcje
}))
),
{ name: 'app-storage' }
),
{ name: 'AppStore' }
)
)Middleware immer umożliwia aktualizacje w stylu mutowalnym, które produkują niemutowalny stan. Middleware subscribeWithSelector pozwala na subskrybowanie fragmentów stanu poza komponentami React.
Gotowy na rozmowy o React / Next.js?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Wzorce integracji TypeScript
Zustand v5 znacząco poprawił wnioskowanie TypeScript. Wzorzec podwójnego wywołania funkcji create<Type>()() zapewnia pełne wnioskowanie typów dla łańcuchów middleware.
Typowanie akcji odnoszących się do innego stanu
interface TodoStore {
todos: Todo[]
filter: 'all' | 'active' | 'completed'
addTodo: (text: string) => void
toggleTodo: (id: string) => void
filteredTodos: () => Todo[]
}
export const useTodoStore = create<TodoStore>()((set, get) => ({
todos: [],
filter: 'all',
addTodo: (text) => set((state) => ({
todos: [...state.todos, { id: crypto.randomUUID(), text, completed: false }]
})),
toggleTodo: (id) => set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
})),
filteredTodos: () => {
const { todos, filter } = get()
switch (filter) {
case 'active': return todos.filter((t) => !t.completed)
case 'completed': return todos.filter((t) => t.completed)
default: return todos
}
}
}))Stan pochodny jako funkcje zamiast getterów unika przestarzałych wartości. Każde wywołanie filteredTodos() odczytuje aktualny stan.
Dzielenie stores dla dużych aplikacji
Częste pytanie rekrutacyjne dotyczy strukturyzacji Zustand pod kątem skalowalności. Wzorzec slice dzieli store na moduły domenowe:
import { StateCreator } from 'zustand'
export interface AuthSlice {
user: User | null
isAuthenticated: boolean
login: (credentials: Credentials) => Promise<void>
logout: () => void
}
export const createAuthSlice: StateCreator<
AuthSlice & CartSlice, // Połączony typ store
[],
[],
AuthSlice
> = (set) => ({
user: null,
isAuthenticated: false,
login: async (credentials) => {
const user = await authApi.login(credentials)
set({ user, isAuthenticated: true })
},
logout: () => set({ user: null, isAuthenticated: false })
})import { create } from 'zustand'
import { createAuthSlice, AuthSlice } from './slices/authSlice'
import { createCartSlice, CartSlice } from './slices/cartSlice'
export const useStore = create<AuthSlice & CartSlice>()((...args) => ({
...createAuthSlice(...args),
...createCartSlice(...args)
}))Slice mogą odnosić się do siebie nawzajem poprzez połączony typ store przekazany do StateCreator.
Popularne pytania rekrutacyjne z wzorcowymi odpowiedziami
Kiedy Zustand byłby złym wyborem?
Zustand dodaje złożoność bez korzyści w tych scenariuszach:
- Statyczne dane konfiguracyjne, które nigdy nie zmieniają się w runtime
- Stan formularza zarządzany przez React Hook Form lub podobne biblioteki
- Stan serwera lepiej obsługiwany przez TanStack Query lub SWR
- Proste przekazywanie props parent-child z 2-3 poziomami zagnieżdżenia
Jak testować komponenty używające Zustand?
Testowanie wymaga resetowania stanu store między testami i opcjonalnie mockowania całego store:
import { act, renderHook } from '@testing-library/react'
import { useCartStore } from './store'
beforeEach(() => {
// Reset store do stanu początkowego
useCartStore.setState({ items: [], totalItems: 0 })
})
test('addItem zwiększa liczbę w koszyku', () => {
const { result } = renderHook(() => useCartStore())
act(() => {
result.current.addItem({ id: '1', name: 'Product', price: 10 })
})
expect(result.current.items).toHaveLength(1)
})Dla testów komponentów, które nie powinny wchodzić w interakcję z prawdziwymi stores, mockuj na poziomie modułu:
import { vi } from 'vitest'
vi.mock('./store', () => ({
useCartStore: vi.fn(() => ({
items: [{ id: '1', name: 'Mock Product', price: 25 }],
addItem: vi.fn()
}))
}))Jak Zustand obsługuje operacje asynchroniczne?
W przeciwieństwie do Redux, Zustand nie wymaga middleware dla async. Akcje mogą być bezpośrednio funkcjami async:
interface ProductStore {
products: Product[]
isLoading: boolean
error: string | null
fetchProducts: () => Promise<void>
}
export const useProductStore = create<ProductStore>((set) => ({
products: [],
isLoading: false,
error: null,
fetchProducts: async () => {
set({ isLoading: true, error: null })
try {
const products = await productApi.getAll()
set({ products, isLoading: false })
} catch (err) {
set({ error: err.message, isLoading: false })
}
}
}))Dla złożonych przepływów asynchronicznych zaleca się trzymanie stanów loading i error w kolokacji z danymi, które opisują.
Zmiany w Zustand v5, które mogą pojawić się na rozmowach
Zustand v5 wprowadził przełomowe zmiany, których rekruterzy używają do oceny, czy kandydaci są na bieżąco:
- Brak domyślnych eksportów: Importuj
{ create }zamiastcreate - Surowszy TypeScript: Wzorzec
create<T>()()zastępujecreate<T>() - Synchroniczna hydratacja persist: Zmieniono timing callbacku
onRehydrateStorage - Usunięto wsparcie CJS: Pakiet tylko ESM
Migracja z v4 do v5 wymaga aktualizacji instrukcji importu i dostosowania kodu, który polegał na starym timingu hydratacji.
Kluczowe wnioski dla rozmów o Zustand
- Store Zustand żyją poza drzewem React, umożliwiając synchroniczny dostęp i zapobiegając zagnieżdżaniu providerów
- Selektory z shallow comparison optymalizują re-rendery dla produkcyjnych aplikacji
- Middleware persist wymaga
partializedo wykluczenia funkcji z storage - Wzorzec slice skaluje Zustand do dużych aplikacji bez poświęcania bezpieczeństwa typów
- Akcje async działają bezpośrednio bez middleware, w przeciwieństwie do wzorców Redux
- Testowanie wymaga jawnego resetu stanu między przypadkami testowymi
- Server Components wymuszają granice klienta dla Zustand, wpływając na decyzje architektoniczne
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Znajdziesz błąd w React / Next.js?
Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Autor:
Anthony Fillion-MailletZałożyciel SharpSkill
Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.
Zaktualizowano 25 sierpnia 2026
Udostępnij
Powiązane artykuły

Next.js 16 Middleware w 2026: Edge Runtime, Uwierzytelnianie i Pytania Rekrutacyjne
Kompleksowy przewodnik po Next.js 16 Middleware obejmujacy Edge Runtime, wzorce uwierzytelniania i pytania rekrutacyjne.

React 19 Suspense i Concurrent Rendering: Streaming SSR oraz pytania rekrutacyjne 2026
Kompleksowy przewodnik po React 19 Suspense, concurrent rendering i streaming SSR. Poznaj use() API, useTransition, useDeferredValue oraz przygotuj się do rozmów rekrutacyjnych.

Testowanie React w 2026: Vitest, React Testing Library i najlepsze praktyki
Kompleksowy przewodnik po testowaniu aplikacji React z użyciem Vitest i React Testing Library. Wzorce testowania komponentów, obsługa operacji asynchronicznych, strategie mockowania oraz praktyki przydatne na rozmowach rekrutacyjnych.