Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает createSlice в Redux Toolkit
createSlice — функция из Redux Toolkit, которая упрощает создание Redux слайсов (reducers + actions). Это один из самых важных API современного Redux.
1. Что такое слайс и зачем он нужен
// Старый подход Redux (вручную создаём actions и reducers)
// actions.js
export const INCREMENT = "counter/INCREMENT";
export const DECREMENT = "counter/DECREMENT";
export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });
// reducers.js
const initialState = { value: 0 };
export function counterReducer(state = initialState, action) {
switch(action.type) {
case INCREMENT: return { ...state, value: state.value + 1 };
case DECREMENT: return { ...state, value: state.value - 1 };
default: return state;
}
}
// Много кода для простой логики!
// Новый подход с createSlice (Redux Toolkit)
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1; // Immer позволяет мутировать!
},
decrement: (state) => {
state.value -= 1;
}
}
});
// Автоматически создаёт actions и reducer
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
2. Структура createSlice
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface CounterState {
value: number;
status: "idle" | "loading";
}
const initialState: CounterState = {
value: 0,
status: "idle"
};
const counterSlice = createSlice({
// name: используется для генерации типов actions
// например: "counter/increment", "counter/decrement"
name: "counter",
// initialState: начальное состояние
initialState,
// reducers: синхронные операции (не требуют async)
// Принимают состояние (state) и payload (данные)
reducers: {
// state можно мутировать благодаря Immer
// payload содержит данные из action
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
// С параметром
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
// С объектом в payload
setCounter: (state, action: PayloadAction<{ value: number; status: string }>) => {
state.value = action.payload.value;
state.status = action.payload.status as any;
},
// Обработка ошибок
reset: (state) => {
state.value = 0;
state.status = "idle";
}
},
// extraReducers: обработчики внешних actions (async thunks, другие slices)
extraReducers: (builder) => {
builder
.addCase(fetchData.pending, (state) => {
state.status = "loading";
})
.addCase(fetchData.fulfilled, (state, action) => {
state.status = "idle";
state.value = action.payload;
})
.addCase(fetchData.rejected, (state) => {
state.status = "idle";
});
}
});
// Экспортируем actions
export const { increment, decrement, incrementByAmount, setCounter, reset } = counterSlice.actions;
// Экспортируем reducer
export default counterSlice.reducer;
3. Использование в компоненте
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement, incrementByAmount } from "./counterSlice";
import { RootState } from "./store";
function Counter() {
// Получаем состояние из Redux
const count = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
{/* Вызов actions из slice */}
<button onClick={() => dispatch(increment())}>
Increment
</button>
<button onClick={() => dispatch(decrement())}>
Decrement
</button>
{/* С параметром */}
<button onClick={() => dispatch(incrementByAmount(5))}>
Add 5
</button>
<button onClick={() => dispatch(setCounter({ value: 10, status: "idle" }))}>
Set to 10
</button>
</div>
);
}
export default Counter;
4. Immer и мутация состояния
// Redux Toolkit использует Immer
// Это позволяет "мутировать" состояние в reducers
const slice = createSlice({
name: "todos",
initialState: {
items: [
{ id: 1, text: "Learn Redux", done: false }
]
},
reducers: {
// Вариант 1: "Мутируем" напрямую (благодаря Immer)
toggleTodo: (state, action: PayloadAction<number>) => {
const todo = state.items.find(item => item.id === action.payload);
if (todo) {
todo.done = !todo.done; // Выглядит как мутация!
}
},
// Вариант 2: Традиционный подход (return новое состояние)
// Оба варианта работают одинаково
addTodo: (state, action: PayloadAction<string>) => {
state.items.push({
id: Date.now(),
text: action.payload,
done: false
});
},
// Вариант 3: Можем использовать стрелочный return
// (Immer не используется в этом случае)
removeTodo: (state, action: PayloadAction<number>) => {
return {
...state,
items: state.items.filter(item => item.id !== action.payload)
};
}
}
});
export const { toggleTodo, addTodo, removeTodo } = slice.actions;
5. Асинхронные операции с createAsyncThunk
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
// createAsyncThunk автоматически создаёт 3 действия:
// - pending (запрос идёт)
// - fulfilled (успех)
// - rejected (ошибка)
export const fetchUsers = createAsyncThunk(
"users/fetchUsers", // название action
async (page: number) => {
const response = await fetch(`/api/users?page=${page}`);
return response.json();
}
);
interface UsersState {
items: any[];
status: "idle" | "loading" | "succeeded" | "failed";
error: string | null;
}
const usersSlice = createSlice({
name: "users",
initialState: {
items: [],
status: "idle",
error: null
} as UsersState,
reducers: {},
extraReducers: (builder) => {
builder
// Запрос начался
.addCase(fetchUsers.pending, (state) => {
state.status = "loading";
})
// Запрос успешен
.addCase(fetchUsers.fulfilled, (state, action) => {
state.status = "succeeded";
state.items = action.payload;
})
// Запрос ошибка
.addCase(fetchUsers.rejected, (state, action) => {
state.status = "failed";
state.error = action.error.message || "Unknown error";
});
}
});
export default usersSlice.reducer;
6. Конфигурация Store
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";
import usersReducer from "./usersSlice";
import todoReducer from "./todoSlice";
export const store = configureStore({
reducer: {
counter: counterReducer,
users: usersReducer,
todo: todoReducer
}
});
// Типизация
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
7. Полный пример с features
// features/counter/counterSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface CounterState {
value: number;
history: number[];
}
const initialState: CounterState = {
value: 0,
history: [0]
};
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
state.history.push(state.value);
},
decrement: (state) => {
state.value -= 1;
state.history.push(state.value);
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
state.history.push(state.value);
},
undo: (state) => {
if (state.history.length > 1) {
state.history.pop();
state.value = state.history[state.history.length - 1];
}
}
}
});
export const { increment, decrement, incrementByAmount, undo } = counterSlice.actions;
export default counterSlice.reducer;
// hooks/useCounter.ts
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement, incrementByAmount, undo } from "../counterSlice";
import { RootState } from "../store";
export function useCounter() {
const value = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch();
return {
value,
increment: () => dispatch(increment()),
decrement: () => dispatch(decrement()),
incrementByAmount: (amount: number) => dispatch(incrementByAmount(amount)),
undo: () => dispatch(undo())
};
}
// Counter.tsx
import { useCounter } from "./hooks/useCounter";
export function Counter() {
const { value, increment, decrement, incrementByAmount, undo } = useCounter();
return (
<div>
<p>Value: {value}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={() => incrementByAmount(5)}>+5</button>
<button onClick={undo}>Undo</button>
</div>
);
}
Рекомендация
createSlice упрощает Redux в 10 раз:
- Меньше кода — не нужно писать action creators вручную
- Immer — мутируй состояние безопасно
- Типизация — PayloadAction обеспечивает полную типизацию
- extraReducers — обработка async операций
- Организация — разделяй по features (counter, users, todo)