Переключайте visibleList через отрицание, started храните в state, а таймером управляйте в эффекте. Сохраняйте List смонтированным либо поднимите его данные выше.
Исправление ошибок в приложении‑генераторе случайных чисел
Переключайте visibleList через отрицание, started храните в state, а таймером управляйте в эффекте. Сохраняйте List смонтированным либо поднимите его данные выше.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
В приложении реализован генератор случайных чисел. Требуется устранить недочёты, указанные в комментариях к файлам, а также добавить возможность удаления чисел из списка.
Требования:
- При нажатии на кнопку «Показать / Скрыть список» список должен корректно отображаться и скрываться.
- Кнопка «Стоп» должна останавливать запущенный таймер.
- При переключении видимости списка его содержимое не должно сбрасываться к начальному массиву
[1, 2, 3]. - Добавьте функциональность удаления отдельного числа из списка.
- По возможности улучшите приложение (производительность, читаемость кода и т.д.).
// App.jsx
import List from "./List";
/*
Генератор случайных чисел.
Описание:
Каждый раз, когда нажимаем на "Добавить число", в конец списка добавляется случайное число.
"Старт" — запускает интервал в 1 сек., который добавляет в конец списка случайное число.
"Стоп" — останавливает таймер.
"Показать / Скрыть" — контролирует отображение приложения.
🔴 Проблемы:
1. При нажатии скрыть/показать список, он не раскрывается. Почему?
2. "Стоп" не останавливает таймер. Почему?
3. При нажатии "Показать / Скрыть" список сбрасывается на [1, 2, 3]. Почему?
4. Реализовать удаление числа
5. Улучшите по максимуму приложение)))
*/
export default function App() {
const [visibleList, setVisibleList] = React.useState(true);
const toggleVisibleList = () => {
setVisibleList(visibleList);
};
return (
<div className="App">
<button onClick={toggleVisibleList}>Показать / Скрыть список</button>
<br />
<br />
<br />
{visibleList && <List />}
</div>
);
}
// List.jsx
import React from "react";
import "./styles.css";
import Buttons from "./Buttons";
export default function List() {
const [numbers, setNumbers] = React.useState([1, 2, 3]);
let timer = null;
let started = false;
const addRandomNumber = () => {
const random = Math.round(Math.random() * 10);
setNumbers((prev) => [...prev, random]);
};
const start = () => {
timer = setInterval(addRandomNumber, 1000);
started = true;
};
const stop = () => {
clearInterval(timer);
started = false;
};
return (
<div className="list">
<Buttons
started={started}
addRandomNumber={() => addRandomNumber()}
onStart={() => start()}
onStop={() => stop()}
/>
<ul>
{numbers.map((num, index) => (
<li key={`${index}_${num}`}>{num}</li>
))}
</ul>
</div>
);
}
// Buttons.jsx
import React from "react";
export default function Buttons({ addRandomNumber, onStart, onStop, started }) {
console.log("render");
return (
<div className="buttons">
<button onClick={addRandomNumber}>Новое число</button>
<br />
<button disabled={started} onClick={onStart}>
Старт
</button>
<button onClick={onStop}>Стоп</button>
</div>
);
}
Ответ
Переключайте visibleList через отрицание, started храните в state, а таймером управляйте в эффекте. Сохраняйте List смонтированным либо поднимите его данные выше.
В App замените условный mount на <List /> и handler на setVisibleList(value => !value). Исправленная логика List:
function List() {
const [numbers, setNumbers] = React.useState(() => [1,2,3].map((value,id) => ({ id, value })));
const [started, setStarted] = React.useState(false);
const nextId = React.useRef(3);
const addRandomNumber = React.useCallback(() => {
const item = { id: nextId.current++, value: Math.floor(Math.random() * 11) };
setNumbers(current => [...current, item]);
}, []);
React.useEffect(() => {
if (!started) return;
const timer = setInterval(addRandomNumber, 1000);
return () => clearInterval(timer);
}, [started, addRandomNumber]);
return <div>
<Buttons started={started} addRandomNumber={addRandomNumber} onStart={() => setStarted(true)} onStop={() => setStarted(false)} />
<ul>{numbers.map(item => <li key={item.id}>{item.value}
<button onClick={() => setNumbers(current => current.filter(x => x.id !== item.id))}>Удалить</button>
</li>)}</ul>
</div>;
}
Hidden сохраняет state и продолжает работающий таймер. Если скрытие должно ещё и останавливать генерацию, добавьте это как отдельное правило. Исходные локальные timer/started терялись при ререндере.