Максимальный размер стека вызовов превышен из цикла

#reactjs

#reactjs

Вопрос:

Я нахожусь на ранних стадиях создания rectangle SVG, который использует форму прямоугольника. Я генерирую цвета пикселей, используя цикл, который получает все цвета RGB с шагом 8. Возвращаемый массив содержит 32 768 <rect /> . Код выдает желаемый результат, однако я получаю ошибку в:

Chrome

 Maximum call stack size exceeded
getTypeSymbol (react_devtools_backend.js:4828)
 

Firefox

 too much recursion
 

Из того, что я могу сказать, это не проблема рекурсии, функция, похоже, не перезагружается. Я думаю, это связано с размером массива.

Любые мысли о том, что я должен здесь делать, я никогда раньше не сталкивался с этой проблемой.

 function PixelColour() {
  console.log("start");
  let counter = 0;
  let x = 0;
  let y = 0;

  const colours = [];

  for (let red = 0; red < 256; red  = 8) {
    for (let green = 0; green < 256; green  = 8) {
      for (let blue = 0; blue < 256; blue  = 8) {
        counter  ;

        if (x < 256) {
          x  ;
        } else {
          x = 0;
        }

        if (x === 256) {
          y  ;
        }

        colours.push(
          <rect
            key={counter}
            x={x}
            y={y}
            height="1"
            width="1"
            style={{ fill: `rgb(${red}, ${green}, ${blue})` }}
          />
        );
      }
    }
  }

  return <Fragment>{colours}</Fragment>;
}

class Colours extends React.Component {
  render() {
    return (
      <div>
        <svg width="256" height="128" style={{ border: "2px solid black" }}>
          <PixelColour />
        </svg>
      </div>
    );
  }
}
 

Ответ №1:

Проблема сама по себе заключается не в перезагрузке (или повторном воспроизведении) вашей функции; вы сталкиваетесь с рекурсией внутри самого React. В частности, с тем, как React devtools пытается согласовать DOM для построения карты компонентов.

На самом деле это не так просто воспроизвести в codesandbox, поскольку, похоже, он использует экспериментальную версию devtools, и эта ошибка возникает только при горячей перезагрузке. Трассировка стека есть scheduleFibersWithFamiliesRecursively , но на моем локальном компьютере со стандартным установленным расширением devtools, которое я получаю mountFiberRecursively при монтировании компонента.

Я немного покопался и наткнулся на эту проблему с github, а также на решение PR, которое, похоже, на данный момент оставлено. Возможно, если вы пойдете и подтолкнете их, они могут взглянуть по-другому:

Получение максимального превышения стека вызовов при backend.js при рендеринге многих элементов.

Переработанный серверный рендерер для удаления большей части рекурсии

Все, что вы можете сделать за это время, это отключить расширение devtools. Я бы добавил, что даже если он отключен, для монтирования этого компонента на моем локальном компьютере требуется несколько секунд. Если вы не выводите вычисление из цикла рендеринга (тело функции), то оно будет выполняться при каждом рендеринге. Вы пытаетесь смонтировать 10 из тысяч узлов DOM, которые никогда не будут работать эффективно — даже приведенный выше PR предполагает ограничение только в 15000.

Я думаю, что лучшей идеей было бы 1) рассчитать это заранее, если вы можете, идеально, как жестко запрограммированные данные и нигде рядом с потоком пользовательского интерфейса, и 2) рисовать на холсте, а не создавать узлы в DOM.