Реализуйте компонент 'Слот-машина' с тремя барабанами: нужно хранить текущие значения барабанов, реализовать запуск вращения (RunSpinner), состояние disabled для кнопки, определить победу/поражение и корректно отображать сообщение, а также сделать компонент масштабируемым на произвольное количество барабанов.

Храните значения барабанов массивом, блокируйте повторный запуск и определяйте результат после завершения всех вращений.

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

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

Храните значения барабанов массивом, блокируйте повторный запуск и определяйте результат после завершения всех вращений.

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

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

Храните значения барабанов массивом, блокируйте повторный запуск и определяйте результат после завершения всех вращений.

Ниже предполагается адаптер runSpinner(index), возвращающий Promise конечного числа и управляющий анимацией. count — положительное целое; при его изменении компонент нужно перемонтировать через key. Ошибки ждём у всех барабанов, чтобы не разблокировать кнопку раньше времени.

import { useEffect, useRef, useState } from 'react';

function SlotMachine({ count = 3, runSpinner }) {
  const [values, setValues] = useState(() => Array(count).fill(null));
  const [running, setRunning] = useState(false);
  const [message, setMessage] = useState('');
  const busy = useRef(false);
  const mounted = useRef(false);
  useEffect(() => {
    mounted.current = true;
    return () => { mounted.current = false; };
  }, []);
  async function run() {
    if (busy.current) return;
    busy.current = true;
    setRunning(true);
    setMessage('');
    const results = await Promise.allSettled(
      Array.from({ length: count }, (_, i) =>
        Promise.resolve().then(() => runSpinner(i)))
    );
    busy.current = false;
    if (!mounted.current) return;
    const next = results.map(r => r.status === 'fulfilled' ? r.value : null);
    setValues(next);
    setRunning(false);
    setMessage(results.some(r => r.status === 'rejected')
      ? 'Ошибка вращения'
      : next.every(v => v === next[0]) ? 'Победа!' : 'Попробуйте ещё');
  }
  return <div>
    <div>{values.map((v, i) => <span key={i}>{v ?? '—'} </span>)}</div>
    <button disabled={running} onClick={run}>Запустить</button>
    <p role="status">{message}</p>
  </div>;
}

Ссылка busy защищает от повторного старта ещё до следующего рендера. Реальный API RunSpinner следует подключить через указанный адаптер.

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

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

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

Подробнее