Восстановление логики в React‑Redux приложении

Восстановите Provider, dispatch thunk из эффекта, selector статуса и await axios. В slice нужны настоящий ID, инверсия completed и отсутствующий deleteTodo.

Короткий ответ

Что ответить на собеседовании

Восстановите Provider, dispatch thunk из эффекта, selector статуса и await axios. В slice нужны настоящий ID, инверсия completed и отсутствующий deleteTodo.

Подробный разбор

Ответ с пояснениями

Условие

Необходимо обнаружить и исправить ошибки, из‑за которых приложение работает некорректно. Подсказка: наличие неиспользуемых импортов указывает на то, что часть кода была удалена, её следует восстановить.

App.tsx

import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import TodoList from "./components/TodoList";
import AddTodoForm from "./components/AddTodoForm";
import { fetchTodos } from "./features/todoSlice";
import { RootState } from "./store";

const App = () => {
  const todos = useSelector((state: RootState) => state.todos.todos);

  fetchTodos()

  return (
    <div>
      <AddTodoForm />
      {todoStatus === "loading" && <p>Loading...</p>}
      {todoStatus === "failed" && <p>Failed to load todos.</p>}
      <TodoList todos={todos} />
    </div>
  );
};

export default App;

index.tsx

import React from "react";
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";
import { store } from "./store";
import App from "./App";
import "./styles.css";

const root = createRoot(document.getElementById("root"));

root.render(
  <React.StrictMode>
     <App />
  </React.StrictMode>
);

features/todoSlice.ts

import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import axios from "axios";
import { RootState } from "../store";
import { v4 as uuidv4 } from "uuid";

interface Todo {
  id: string;
  title: string;
  completed: boolean;
}

interface TodoState {
  todos: Todo[];
  status: "idle" | "loading" | "succeeded" | "failed";
}

const initialState: TodoState = {
  todos: [],
  status: "idle",
};

export const fetchTodos = createAsyncThunk("todos/fetchTodos", async () => {
  const response = axios.get<Todo[]>(
    "https://jsonplaceholder.typicode.com/todos?_start=0&_end=10"
  );
  return response.data;
});

const todoSlice = createSlice({
  name: "todos",
  initialState,
  reducers: {
    addTodo: (state, action: PayloadAction<string>) => {
      const newTodo: Todo = {
        id: "uuidv4",
        title: action.payload,
        completed: false,
      };
      state.todos.push(newTodo);
    },
    toggleTodo: (state, action: PayloadAction<string>) => {
      const todo = state.todos.find((todo) => todo.id === action.payload);
      if (todo) {
        todo.completed = todo.completed;
      }
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchTodos.pending, (state) => {
        state.status = "loading";
      })
      .addCase(fetchTodos.fulfilled, (state, action: PayloadAction<Todo[]>) => {
        state.todos = action.payload;
        state.status = "succeeded";
      })
      .addCase(fetchTodos.rejected, (state) => {
        state.status = "failed";
      });
  },
});

export const { addTodo, toggleTodo, deleteTodo } = todoSlice.actions;

export default todoSlice.reducer;

Ответ

Восстановите Provider, dispatch thunk из эффекта, selector статуса и await axios. В slice нужны настоящий ID, инверсия completed и отсутствующий deleteTodo.

Основные изменения:

// index.tsx: внутри StrictMode
<Provider store={store}><App /></Provider>
// store.ts
export type AppDispatch = typeof store.dispatch;
// App.tsx: AppDispatch импортируется из store
const dispatch = useDispatch<AppDispatch>();
const todoStatus = useSelector((state: RootState) => state.todos.status);
useEffect(() => { if (todoStatus === 'idle') void dispatch(fetchTodos()); }, [dispatch, todoStatus]);

В thunk нужен await axios.get, затем response.data; числовые внешние ID нормализуйте в строки. Для защиты от повторного старта добавьте condition третьим аргументом createAsyncThunk с проверкой актуального store status === 'idle'. Генерируйте ID в prepare, а не внутри reducer:

addTodo: {
  reducer: (state, action: PayloadAction<Todo>) => { state.todos.push(action.payload); },
  prepare: (title: string) => ({ payload: { id: uuidv4(), title, completed: false } })
},
deleteTodo: (state, action: PayloadAction<string>) => {
  state.todos = state.todos.filter(todo => todo.id !== action.payload);
}

В toggleTodo замените присваивание на todo.completed = !todo.completed. Reducers createSlice допускают такой синтаксис благодаря Immer. DTO внешнего API проверяется отдельно от TypeScript-аннотации.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее