React: доступ к внутренней очереди операций React

#reactjs

Вопрос:

React собирает операции(например, операции DOM, такие как «ДОБАВИТЬ», «ЗАМЕНИТЬ», «УДАЛИТЬ» и т. Д.), Чтобы они могли выполнять их в пакетном режиме одним выстрелом в конце каждого рендеринга.

например, вызов setState внутри компонента React запланирован на конец построения этого дерева реакций путем добавления этой операции в очередь операций. затем react просмотрит эту очередь и решит, какие изменения необходимо внести в DOM.

React решит, вызывать или нет другой рендеринг, основываясь на том, пуста ли очередь операций или нет.

более подробная информация об этом потрясающем уроке, в котором кратко излагаются основы того, как внутренне работает React.

Мне нужен доступ к этой очереди, чтобы решить, был ли текущий рендеринг последним для очень настраиваемого компонента React. (ДА, возможно, я могу избежать этого, но в настоящее время это мое требование)
доступ должен быть изнутри этого компонента.

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

не удалось найти никакого общедоступного API, но обходной путь также был бы приемлемым. (разветвление и редактирование React не является обходным путем)

этот файл src, вероятно, является основной логикой этой очереди. и это тип реальной очереди. однако это исходный код, и эта очередь не экспортируется во встроенных версиях react(ни в разработке, ни в производственной сборке).

итак, есть ли способ получить доступ к внутренней очереди операций React?

Ответ №1:

ИЗМЕНИТЬ — Предупреждение

это только в образовательных целях — не используйте его на производстве! этот подход небезопасен для члена основной команды React — я уже спрашивал. это может быть безопасно, если вы планируете использовать исправленную версию React без последующего обновления.

####### КОНЕЦ РЕДАКТИРОВАНИЯ #######

РЕШЕНО!

поэтому после многих-многих часов копания в кодовой базе React я, наконец, написал хук, который сообщает, запланировано ли в настоящее время какое-либо обновление.

Примечание: будет работать только для функциональных компонентов, и этот крючок недостаточно хорошо протестирован.

вы можете видеть некоторое внутреннее состояние React по свойству undocumented __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED . эта опора выполняется ReactCurrentOwner , что в основном является ссылкой на текущий компонент, который в настоящее время создается.

 const currentOwner = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner?.current;
 

currentOwner является текущим компонентом, который создается. эта поддержка доступна только во время рендеринга(потому что в эффектах после рендеринга в настоящее время не создается ни один компонент).
но поскольку другой рендеринг может быть запущен из набора состояний из эффектов, мы всегда должны вызывать проверку цветов из последнего эффекта

внутри него .current.memoizedProps вы найдете связанный список всех крючков, которые были объявлены до этого момента в этом компоненте.

каждый крючок содержит значок queue для хранения запланированных обновлений, а внутри него есть pending опора, которая сообщает, запланировано ли какое-либо обновление в настоящее время для следующего рендеринга.

мы могли бы просмотреть этот связанный список, чтобы узнать, запланировано ли обновление каким-либо крючком:

 const wouldUpdate = (currentOwner) => {
  let newObj = currentOwner?.memoizedState;
  // go over the linked list of hooks and find out if there is any pending update
  while (newObj amp;amp; 'next' in newObj) {
    newObj = newObj['next'];
    if (newObj?.queue?.pending) return true;
  }
  return false;
};

 

поэтому летом мы могли бы создать пользовательский крюк, чтобы проверить, является ли текущая визуализация последней запланированной визуализацией:

 const wouldUpdate = (currentOwner) => {
  let newObj = currentOwner?.memoizedState;
  // go over the linked list of hooks and find out if there is any pending update
  while (newObj amp;amp; 'next' in newObj) {
    newObj = newObj['next'];
    if (newObj?.queue?.pending) return true;
  }
  return false;
};

export const useDoesUpdateIsScheduled = () => {
  // @ts-ignore
  // hold the current owner ref so we could call it from effects
  const currentOwner = useRef(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner?.current);
  return () => wouldUpdate(currentOwner.current);
};
 

так много часов за такой маленький код… ;

Использование:

 const YourComponent = (props) => {
  //..
  // code, hooks ,logic, effects would be here
  //..

  // should be could from the last useEffect
  const wouldUpdate = useDoesUpdateIsScheduled();
  useEffect(() => {
    console.log(wouldUpdate());
  });

  return <div>... your jsx here ...</div>;
};
 

снимок экрана тестового компонента на монтировании:

введите описание изображения здесь

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

вы также можете вызывать wouldUpdate из тела функции, но учтите, что обновления могут быть запланированы из эффектов (это означает, что вызов его во время визуализации не будет перехватывать эти обновления)

популярное приложение «почему ты сделал рендеринг» также использует это недокументированное __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED свойство для достижения своей цели.

ВОТ и ВСЕ(на самом деле это не стоило потраченных часов, и это, вероятно, сломается в разных случаях, так как это не публичный API).