Irbisa · Шпаргалка
Управление состоянием
Локальное, глобальное и серверное состояние, Context и цена его ререндеров, Redux Toolkit, Zustand, Jotai, URL как состояние, выбор стора
Чем отличаются локальное, глобальное и серверное состояние в React-приложении?
Объясните однонаправленный поток данных в Redux. Почему редьюсеры должны быть чистыми?
Может ли React Context заменить Redux или другой стейт-менеджер?
Найдите баг: ввод в поле поиска перерендеривает каждый компонент, который читает текущего пользователя.
export function AppProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState<User | null>(null); const [query, setQuery] = useState(''); return ( <AppContext value={{ user, setUser, query, setQuery }}> {children} </AppContext> );} function Avatar() { const { user } = use(AppContext); return <img src={user?.avatarUrl} alt="" />;}Какие проблемы решает Redux Toolkit по сравнению с Redux, написанным вручную?
import { configureStore, createSlice, type PayloadAction } from '@reduxjs/toolkit';import { useDispatch, useSelector } from 'react-redux'; const cartSlice = createSlice({ name: 'cart', initialState: { items: [] as { id: string; qty: number }[] },…Найдите баг: диспатч
todosClearedзапускает редьюсер, но список остаётся на экране.const todosSlice = createSlice({ name: 'todos', initialState: { items: [] as Todo[], filter: 'all' as Filter }, reducers: { todoAdded(state, action: PayloadAction<Todo>) { state.items.push(action.payload); }, todosCleared(state) { state = { items: [], filter: state.filter }; }, },});Как работают подписки
useSelectorи почему селектор может вызывать лишние ререндеры?import { createSelector } from '@reduxjs/toolkit';import { useSelector } from 'react-redux'; const selectItems = (state: RootState) => state.todos.items;const selectFilter = (state: RootState) => state.todos.filter;…Где в Redux Toolkit живут асинхронная логика и побочные эффекты?
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; export const fetchUser = createAsyncThunk('user/fetch', async (id: string, { signal }) => { const res = await fetch(`/api/users/${id}`, { signal }); if (!res.ok) throw new Error(`HTTP ${res.status}`); return (await res.json()) as User;…Что такое RTK Query и когда выбирать его вместо TanStack Query?
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; export const api = createApi({ baseQuery: fetchBaseQuery({ baseUrl: '/api' }), tagTypes: ['Todo'], endpoints: (build) => ({…Как хранить в Redux связанные данные и списки и что даёт
createEntityAdapter?import { createEntityAdapter, createSlice, type PayloadAction } from '@reduxjs/toolkit'; interface Post { id: string; title: string; authorId: string; // a reference, not a nested user object…Что такое Zustand и чем он отличается от Redux Toolkit?
import { create } from 'zustand'; interface CartState { items: string[]; add: (id: string) => void; clear: () => void;…Найдите баг: после обновления до Zustand 5 этот компонент падает с ошибкой «Maximum update depth exceeded».
function CartSummary() { const { items, clear } = useCart((s) => ({ items: s.items, clear: s.clear })); return ( <div> {items.length} items <button onClick={clear}>Clear</button> </div> );}Что такое атомарный подход к состоянию (Jotai) и когда он подходит лучше единого стора?
import { atom, useAtom, useAtomValue } from 'jotai'; const priceAtom = atom(100);const qtyAtom = atom(1);const totalAtom = atom((get) => get(priceAtom) * get(qtyAtom)); // derived, read-only…Что такое MobX и чем его модель наблюдаемого состояния отличается от Redux?
class CartStore { items: { id: string; price: number; qty: number }[] = []; constructor() { makeAutoObservable(this); // fields: observable, getters: computed, methods: actions }…Как сохранять состояние стора между перезагрузками страницы и что может пойти не так?
import { create } from 'zustand';import { persist } from 'zustand/middleware'; type Theme = 'light' | 'dark'; interface PrefsState {…Когда состояние стоит хранить в URL, а не в стейте React или в сторе?
Почему копировать ответы API в Redux или Context обычно считается антипаттерном и что использовать вместо этого?
Как сохранить стор поддерживаемым в большом приложении со множеством фич и команд?
Как бы вы поэтапно перевели большое приложение со старого Redux (
connect, саги, редьюсеры, написанные вручную) на Redux Toolkit или TanStack Query?import { configureStore } from '@reduxjs/toolkit';import createSagaMiddleware from 'redux-saga';import { legacyReducers } from './legacy/reducers'; // hand-written reducers, untouchedimport { rootSaga } from './legacy/sagas';import { cartSlice } from './features/cart/cartSlice'; // migrated to createSliceimport { api } from './services/api'; // RTK Query, filled endpoint by endpoint…Когда конечный автомат (XState) моделирует состояние UI лучше, чем булевы флаги в сторе, и чего это стоит?
// flags: { isSubmitting, isError, isSuccess } allow 8 combinations, only 4 make sense type State = 'idle' | 'submitting' | 'failed' | 'succeeded';type Event = 'SUBMIT' | 'RESOLVE' | 'REJECT' | 'RESET'; // one state at a time, and each state lists the events it accepts…Как бы вы выбирали подход к управлению состоянием для нового React-приложения?