getItems и deleteItem уже реализуют основную логику. createItem должен возвращать копию и избегать ID, уже находящихся в store: иначе счётчик однажды столкнётся с исходным id 666.
Task manager implementation
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 восстанавливается: это демонстрация, не постоянное серверное хранилище.