Одне React-завдання, яке демонструє ключові навички на співбесіді

React

Співбесіди на позиції React-розробника часто включають тестове завдання чи live-coding, де потрібно за короткий час продемонструвати свої знання. Одне з найкращих способів оцінити кандидата – дати йому цілісне завдання, яке покриє кілька ключових аспектів React: роботу зі станом, взаємодію з API, обробку подій та оптимізацію. У цій статті ми розглянемо одне завдання, яке можна попросити виконати на співбесіді, і розберемо, чому воно показує ключові навички.

Опис завдання

  1. Завдання: Створити простий TODO-додаток із можливістю:
    • Додавати нові завдання;
    • Відмічати їх як виконані;
    • Видаляти їх із списку;
    • Зберігати та отримувати список завдань із фейкового API (можна використати mock API або json-server).
  2. Вимоги:
    • Використати React Hooks (useState, useEffect) або Redux (залежно від вимог співбесіди);
    • Зробити форму додавання завдання;
    • Продемонструвати обробку помилок (наприклад, якщо API недоступний).
  3. Оптимізація:
    • Реалізувати мінімальний кеш або показати, як уникнути зайвих запитів;
    • Пояснити підхід до мемоізації (наприклад, useMemo або React.memo).
  4. UI (базовий або із бібліотекою Material UI / Bootstrap) – не критично, головне показати інтуїтивну взаємодію.

Чому саме TODO-додаток?

  1. Розуміння стану (state management): TODO-список відмінно демонструє роботу зі станом: додавання, видалення й оновлення елементів.
  2. Обробка користувацьких подій: Додавання завдання пов’язане з формою, а відмітка “cвиконано” – з чекбоксом.
  3. Взаємодія з API: Можливість завантажувати завдання з сервера та відправляти зміни.
  4. Оптимізація: Якщо додаток стає більшим, потрібно думати про ефективні ререндери.

Приклад рішення

1. Структура проєкту

src/
  components/
    TodoList.js
    TodoItem.js
    AddTodoForm.js
  App.js
  api.js

Пояснення:

  • TodoList.js: відображає список завдань.
  • TodoItem.js: окремий компонент для кожного пункту.
  • AddTodoForm.js: компонент із формою додавання нового завдання.
  • api.js: модуль для взаємодії з API (запити та обробка помилок).
  • App.js: головний компонент, координує всі дії.

2. API (mock-варіант)

api.js – імітуємо роботу з сервером:

// api.js
const todos = [
  { id: 1, text: 'Вивчити React', completed: false },
  { id: 2, text: 'Прочитати статтю про Redux', completed: true },
];

// Імітація затримки
const delay = (ms) => new Promise((res) => setTimeout(res, ms));

export async function getTodos() {
  await delay(500); // імітація мережевої затримки
  return todos;
}

export async function addTodo(text) {
  await delay(300);
  const newTodo = { id: Date.now(), text, completed: false };
  todos.push(newTodo);
  return newTodo;
}

export async function toggleTodo(id) {
  await delay(200);
  const todo = todos.find((t) => t.id === id);
  if (todo) {
    todo.completed = !todo.completed;
  }
  return todo;
}

export async function deleteTodo(id) {
  await delay(200);
  const index = todos.findIndex((t) => t.id === id);
  if (index !== -1) {
    todos.splice(index, 1);
  }
  return { success: true };
}

3. Головний компонент App.js

// App.js
import React, { useState, useEffect } from 'react';
import { getTodos, addTodo, toggleTodo, deleteTodo } from './api';
import TodoList from './components/TodoList';
import AddTodoForm from './components/AddTodoForm';

function App() {
  const [todos, setTodos] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        const data = await getTodos();
        setTodos(data);
      } catch (err) {
        setError('Не вдалося завантажити завдання');
      } finally {
        setLoading(false);
      }
    }
    fetchData();
  }, []);

  const handleAddTodo = async (text) => {
    try {
      const newTodo = await addTodo(text);
      setTodos((prev) => [...prev, newTodo]);
    } catch (err) {
      setError('Помилка при додаванні завдання');
    }
  };

  const handleToggleTodo = async (id) => {
    try {
      const updated = await toggleTodo(id);
      setTodos((prev) =>
        prev.map((t) => (t.id === updated.id ? updated : t))
      );
    } catch (err) {
      setError('Помилка при зміні статусу завдання');
    }
  };

  const handleDeleteTodo = async (id) => {
    try {
      await deleteTodo(id);
      setTodos((prev) => prev.filter((t) => t.id !== id));
    } catch (err) {
      setError('Помилка при видаленні завдання');
    }
  };

  if (loading) return <p>Завантаження...</p>;
  if (error) return <p>{error}</p>;

  return (
    <div>
      <h1>Мій TODO-додаток</h1>
      <AddTodoForm onAddTodo={handleAddTodo} />
      <TodoList
        todos={todos}
        onToggle={handleToggleTodo}
        onDelete={handleDeleteTodo}
      />
    </div>
  );
}

export default App;

Ключові моменти:

  • useEffect() – завантаження списку завдань під час старту додатку.
  • Обробка помилок через try/catch.
  • Окремі функції для додавання, видалення та зміни статусу завдання.

4. Компонент AddTodoForm

// AddTodoForm.js
import React, { useState } from 'react';

function AddTodoForm({ onAddTodo }) {
  const [text, setText] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!text.trim()) return;
    onAddTodo(text);
    setText('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Нове завдання"
        value={text}
        onChange={(e) => setText(e.target.value)}
      />
      <button type="submit">Додати</button>
    </form>
  );
}

export default AddTodoForm;

Що тут демонструє?

  • Роботу з формою
  • Роботу зі станом (text)
  • Передачу callback onAddTodo

5. Компонент TodoList і TodoItem

// TodoList.js
import React from 'react';
import TodoItem from './TodoItem';

function TodoList({ todos, onToggle, onDelete }) {
  if (!todos.length) {
    return <p>Завдань поки що немає</p>;
  }

  return (
    <ul>
      {todos.map((todo) => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggle={onToggle}
          onDelete={onDelete}
        />
      ))}
    </ul>
  );
}

export default TodoList;
// TodoItem.js
import React from 'react';

function TodoItem({ todo, onToggle, onDelete }) {
  return (
    <li>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={() => onToggle(todo.id)}
      />
      <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
        {todo.text}
      </span>
      <button onClick={() => onDelete(todo.id)}>Видалити</button>
    </li>
  );
}

export default TodoItem;

Що тут демонструє?

  • Розділення логіки між компонентами
  • Використання callback’ів для зміни стану (toggle, delete)

Чому таке завдання демонструє ключові навички?

  1. Робота зі станом (state management)
    React-хуки (useState, useEffect) дають уявлення, як розробник керує станом.
  2. Взаємодія з API
    Показує, як обробляються асинхронні запити, помилки та завантаження.
  3. Обробка подій
    Форма додавання і чекбокси – класичні приклади.
  4. Оптимізація
    Хоча у прикладі немає складної оптимізації, але можна побачити, чи відокремлює розробник компоненти, аби уникати зайвих рендерів.
  5. Обробка помилок
    Наявність try/catch для запитів до API.
  6. Побудова UI
    Використання простих компонентів (список, кнопки, поля вводу).

Можливі покращення та питання на співбесіді

  1. Сортування чи фільтрація
    Додати функціонал сортування за станом (виконані/невиконані).
  2. Валідація
    Перед додаванням завдання можна перевіряти довжину тексту.
  3. Оптимізація рендеру
    Використання React.memo для TodoItem.
  4. Тестування
    Запитати, як кандидат писав би юніт-тести.
  5. Альтернативні рішення (Redux, Context)
    Як розподіляти логіку, якщо програма стає більшою?

Висновок

Одне завдання з TODO-додатком покриває багато аспектів React: від роботи зі станом і API до структурованого коду й обробки помилок. На співбесіді воно дає змогу швидко зрозуміти рівень кандидата, його підхід до архітектури та вміння справлятися з асинхронними сценаріями. Якщо ви готуєтесь до співбесіди – виконайте це завдання самостійно, а якщо ви інтерв’юер – задайте його кандидату та зверніть увагу на деталі реалізації.