Как импортировать слайс в Redux-стор?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое Redux Slice
Redux Slice - это набор редьюсера, экшнов и состояния, объединённые в один модуль. Это часть Redux Toolkit (@reduxjs/toolkit), которая значительно упростила работу с Redux, избавив разработчиков от необходимости писать множество boilerplate кода.
Структура Redux Slice
// src/features/user/userSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface UserState {
name: string;
age: number;
email: string;
}
const initialState: UserState = {
name: 'Иван',
age: 28,
email: 'ivan@example.com'
};
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
setName: (state, action: PayloadAction<string>) => {
state.name = action.payload;
},
setAge: (state, action: PayloadAction<number>) => {
state.age = action.payload;
},
updateUser: (state, action: PayloadAction<UserState>) => {
return action.payload; // Можно заменить весь state
}
}
});
export const { setName, setAge, updateUser } = userSlice.actions;
export default userSlice.reducer;
Как импортировать Slice в Store
Базовое объединение слайсов
// src/store/store.ts
import { configureStore } from '@reduxjs/toolkit';
import userReducer from '../features/user/userSlice';
import postsReducer from '../features/posts/postsSlice';
import authReducer from '../features/auth/authSlice';
const store = configureStore({
reducer: {
user: userReducer,
posts: postsReducer,
auth: authReducer
}
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
Импорт reducer из slice
Когда вы вызываете createSlice, он автоматически создаёт reducer. Экспортируйте его по умолчанию (default export) из файла slice:
// src/features/comments/commentsSlice.ts
const commentsSlice = createSlice({
name: 'comments',
initialState: [],
reducers: { /* ... */ }
});
export default commentsSlice.reducer; // Главный экспорт для store
Тогда в store импортируйте просто:
import commentsReducer from '../features/comments/commentsSlice';
Практический пример: Полная структура
1. Создаём слайс
// src/features/todos/todosSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface Todo {
id: string;
title: string;
completed: boolean;
}
interface TodosState {
items: Todo[];
loading: boolean;
error: string | null;
}
const initialState: TodosState = {
items: [],
loading: false,
error: null
};
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
addTodo: (state, action: PayloadAction<Todo>) => {
state.items.push(action.payload);
},
removeTodo: (state, action: PayloadAction<string>) => {
state.items = state.items.filter(todo => todo.id !== action.payload);
},
toggleTodo: (state, action: PayloadAction<string>) => {
const todo = state.items.find(t => t.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
},
setLoading: (state, action: PayloadAction<boolean>) => {
state.loading = action.payload;
}
}
});
export const { addTodo, removeTodo, toggleTodo, setLoading } = todosSlice.actions;
export default todosSlice.reducer;
2. Создаём store с импортированным слайсом
// src/store/store.ts
import { configureStore } from '@reduxjs/toolkit';
import todosReducer from '../features/todos/todosSlice';
const store = configureStore({
reducer: {
todos: todosReducer
}
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
3. Используем в приложении
// src/App.tsx
import { Provider } from 'react-redux';
import store from './store/store';
import TodosContainer from './features/todos/TodosContainer';
function App() {
return (
<Provider store={store}>
<TodosContainer />
</Provider>
);
}
export default App;
4. Используем в компонентах
// src/features/todos/TodosContainer.tsx
import { useDispatch, useSelector } from 'react-redux';
import { RootState, AppDispatch } from '../../store/store';
import { addTodo, removeTodo, toggleTodo } from './todosSlice';
function TodosContainer() {
const dispatch = useDispatch<AppDispatch>();
const todos = useSelector((state: RootState) => state.todos.items);
const loading = useSelector((state: RootState) => state.todos.loading);
const handleAddTodo = (title: string) => {
dispatch(addTodo({
id: Date.now().toString(),
title,
completed: false
}));
};
const handleToggle = (id: string) => {
dispatch(toggleTodo(id));
};
const handleRemove = (id: string) => {
dispatch(removeTodo(id));
};
return (
<div>
<h1>Мои задачи</h1>
{loading && <p>Загрузка...</p>}
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => handleToggle(todo.id)}
/>
{todo.title}
<button onClick={() => handleRemove(todo.id)}>Удалить</button>
</li>
))}
</ul>
</div>
);
}
export default TodosContainer;
Несколько слайсов в одном стора
// src/store/store.ts
import { configureStore } from '@reduxjs/toolkit';
import userReducer from '../features/user/userSlice';
import authReducer from '../features/auth/authSlice';
import todosReducer from '../features/todos/todosSlice';
import postsReducer from '../features/posts/postsSlice';
import notificationsReducer from '../features/notifications/notificationsSlice';
const store = configureStore({
reducer: {
user: userReducer,
auth: authReducer,
todos: todosReducer,
posts: postsReducer,
notifications: notificationsReducer
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware()
.concat(/* custom middleware если нужно */)
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
Советы и лучшие практики
Типизация состояния
// Создайте отдельный файл для типов
// src/features/user/types.ts
export interface User {
id: string;
name: string;
email: string;
role: 'admin' | 'user';
}
export interface UserState {
data: User | null;
loading: boolean;
error: string | null;
}
Селекторы для удобного доступа
// src/features/user/userSlice.ts
export const selectUser = (state: RootState) => state.user.data;
export const selectUserLoading = (state: RootState) => state.user.loading;
export const selectUserError = (state: RootState) => state.user.error;
// В компоненте
const user = useSelector(selectUser);
Асинхронные операции с Thunk
import { createAsyncThunk } from '@reduxjs/toolkit';
export const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId: string, { rejectWithValue }) => {
try {
const response = await fetch(`/api/users/${userId}`);
return await response.json();
} catch (error) {
return rejectWithValue('Ошибка загрузки');
}
}
);
// В слайсе
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string;
});
}
Выводы
Для импорта слайса в Redux стор: создайте слайс с createSlice, экспортируйте reducer по умолчанию, импортируйте его в configureStore с уникальным ключом. Redux Toolkit значительно упростил работу с Redux, исключив необходимость в action creators и типовом коде. Используйте типизацию TypeScript для надёжности.