Исправление ошибок в приложении‑генераторе случайных чисел

Переключайте 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 терялись при ререндере.

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

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

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

Подробнее