React: Получить порядок компонентов в иерархии

Я пытаюсь реализовать вспомогательные компоненты для CSS Grids. У меня есть что-то вроде (подготовьтесь):

<ColumnContainer columns={[ "1em", "1fr", "auto", "auto", "1em", "auto"]}>
  <ColumnContainer.Row>
    { rowIdx => (
      <div style={{ gridRow: rowIdx, gridColumn: "1 / 3" }}>
      </div>
    )}
  </ColumnContainer.Row>
</ColumnContainer>

ColumnContainer:

  • Является контейнером div с display: grid и различные свойства сетки настроены
  • Является также провайдером контекста

Затем, ColumnContainer.Row:

  • Является в основном потребителем контекста
  • Выполняет функцию ребенка
  • Не обязательно быть прямым потомком ColumnContainer - следовательно, используя контекст

Предоставленный контекст представляет собой целое число, layoutVersion, которое увеличивается при каждом изменении набора строк (для запуска повторной визуализации) и - взлома хаков - пустого массива.

Идея состоит в том, что каждый ColumnContainer.Row добавляет себя (может быть любой объект) к массиву, ссылка которого передается в контексте, и отображает дочернюю функцию с размером массива в качестве параметра (индекс строки).

Верьте или нет, это работает, для первого рендера и если строки просто добавлены в конец.

Однако, когда компоненты добавляются в "середине" компонента DOM, результирующие отображаемые строки выходят из строя (но не перекрываются). Смысл, я думаю, что в случае новой версии макета (повторной визуализации) все ColumnContainer.Row повторно отображаются, но не обязательно в "естественном" порядке, в котором они находятся, т.е. В DOM.

Моя догадка заключается в том, что в зависимости от компонентов, вызываемых в определенном порядке render() это плохая идея, а также изменение содержимого свойств контекста в render().

Каковы мои другие варианты - я действительно хочу знать "естественный" порядок узлов-потомков в дереве компонентов. Если бы они были прямыми дочерними элементами, я бы предположил, что это будет легко - в моем случае, хотя у меня есть вложенные компоненты, которые могут выводить строки.

Ответ 1

Я нашел изворотливое/взломанное решение, имея собственный регистр дочерних компонентов (уникальный идентификатор и ref) с компонентом контейнера посредством обратного вызова, введенного в контекст.

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

Контейнер componentDidUpdate проверяет флаг re-calc и сравнивает все примечания DOM (через ref) с помощью функции DOM compareDocumentPosition. Следующее обновление состояния очищает флаг перевыпуска и включает в себя порядок каждого из идентификаторов детей.

Затем это состояние передается всем детям через контекст, где они могут искать свой индекс по идентификатору.

Ясно, что это отстой, но все, что у меня есть на данный момент. По-прежнему хотел бы наградить награду лучшим решением.

Ответ 2

Кажется, что вы полагаетесь на render() чтобы предоставить вам рекурсивную структуру внутри вашего DOM Tree. Причина, по которой она работает "в первый раз", заключается в том, что все компоненты должны быть визуализированы в первый раз, когда они будут представлены для react.

Однако последующие вызовы render() на ColumnContainer не подразумевают рендеринг на подкомпонентах, потому что их state или props могут не измениться.

Я думаю, вы должны попробовать следующее:

  1. Изменение ColumnContainer.Row регистрации на ColumnContainer массив таким образом, чтобы дети rembember rowIdx в их state на первый вызов, а затем повторно использовать это значение для последующих render вызовов.

(чтобы сделать это эффективно, вам понадобится :)

  1. Измените процедуру регистрации в двух вызовах функций (эти две функции должны быть в вашем контексте):

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