Task manager implementation

getItems и deleteItem уже реализуют основную логику. createItem должен возвращать копию и избегать ID, уже находящихся в store: иначе счётчик однажды столкнётся с исходным id 666.

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

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

getItems и deleteItem уже реализуют основную логику. createItem должен возвращать копию и избегать ID, уже находящихся в store: иначе счётчик однажды столкнётся с исходным id 666.

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

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

Условие

Создайте приложение списка дел, использующее определённые ниже методы. Компонент UI должен отображаться внутри элемента <main>. Вспомогательные функции sleep, getId и интерфейс TodoItem предоставлены, вместе с хранилищем store в памяти. Реализуйте асинхронные функции getItems, createItem и deleteItem, которые моделируют задержку с помощью sleep и работают с хранилищем. Функции должны возвращать копии элементов, добавлять новые элементы с уникальными id и временными метками, а также удалять элементы по id, выбрасывая ошибку, если id не существует.

const App = () => {
  return <main></main>;
};

export default App;

export function sleep(ms: number) {
  return new Promise((res) => setTimeout(res, ms));
}

let count = 0;

function getId() {
  return count++;
}

export interface TodoItem {
  id: number;
  title: string;
  complete: boolean;
  createAt: number;
}

const store: TodoItem[] = [
  {
    id: 666,
    title: "default",
    complete: false,
    createAt: 123
  }
];

export async function getItems() {
  await sleep(1_000);
  // клонируем элементы, чтобы безопасно мутировать их в store
  return store.map((item) => ({ ...item }));
}

export async function createItem(title: string) {
  await sleep(1_000);

  const item = {
    id: getId(),
    title,
    complete: false,
    createAt: Date.now()
  };

  store.push(item);

  return item;
}

export async function deleteItem(id: number) {
  await sleep(1_000);

  const storedItemIndex = store.findIndex((item) => item.id === id);

  if (storedItemIndex === -1) {
    throw new Error("item not found");
  }

  store.splice(storedItemIndex, 1);
}

Ответ

getItems и deleteItem уже реализуют основную логику. createItem должен возвращать копию и избегать ID, уже находящихся в store: иначе счётчик однажды столкнётся с исходным id 666.

Исправленная операция создания:

export async function createItem(title: string): Promise<TodoItem> {
  const text = title.trim();
  if (!text) throw new Error('Введите название');
  await sleep(1000);
  let id = getId();
  while (store.some(item => item.id === id)) id = getId();
  const item: TodoItem = { id, title: text, complete: false, createAt: Date.now() };
  store.push(item);
  return { ...item };
}

Минимальный UI использует существующие три функции:

function App() {
  const [items, setItems] = React.useState<TodoItem[]>([]);
  const [title, setTitle] = React.useState('');
  const [busy, setBusy] = React.useState(true);
  const [error, setError] = React.useState('');
  React.useEffect(() => {
    let active = true;
    getItems().then(data => { if (active) setItems(data); })
      .catch(() => { if (active) setError('Ошибка загрузки'); })
      .finally(() => { if (active) setBusy(false); });
    return () => { active = false; };
  }, []);
  async function change(id?: number) {
    if (busy) return;
    setBusy(true); setError('');
    try {
      if (id === undefined) {
        const item = await createItem(title);
        setItems(previous => [...previous, item]); setTitle('');
      } else {
        await deleteItem(id);
        setItems(previous => previous.filter(item => item.id !== id));
      }
    } catch (e) { setError(e instanceof Error ? e.message : 'Ошибка'); }
    finally { setBusy(false); }
  }
  return <main>
    <form onSubmit={e => { e.preventDefault(); void change(); }}>
      <label>Задача <input value={title} disabled={busy} onChange={e => setTitle(e.target.value)} /></label>
      <button disabled={busy || !title.trim()}>Добавить</button>
    </form>
    {busy && <p role="status">Загрузка…</p>}{error && <p role="alert">{error}</p>}
    <ul>{items.map(item => <li key={item.id}>{item.title}
      <button disabled={busy} onClick={() => void change(item.id)}>Удалить</button>
    </li>)}</ul>
  </main>;
}

Нужен импорт React. После перезагрузки in-memory store восстанавливается: это демонстрация, не постоянное серверное хранилище.

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

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

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

Подробнее