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
могут не измениться.
Я думаю, вы должны попробовать следующее:
- Изменение
ColumnContainer.Row
регистрации на ColumnContainer
массив таким образом, чтобы дети rembember rowIdx
в их state
на первый вызов, а затем повторно использовать это значение для последующих render
вызовов.
(чтобы сделать это эффективно, вам понадобится :)
-
Измените процедуру регистрации в двух вызовах функций (эти две функции должны быть в вашем контексте):
- функция, которая получает
rowIdx
для ребенка и запоминает его. (эта функция вызывается только в том случае, если у вас нет кэшированного значения для rowIdx
) - вторая функция, которая выполняет работу, но не нуждается в
rowIdx
в качестве параметра. (эта функция называется так же, как и сейчас, каждый раз).