почему дальше у нас сначала идет лог из рефа ребенка, а только потом из рефа родителя - непонятно
назначение рефов происходит на том же этапе, что и выполнение колбэка в useLayoutEffect: синхронно сразу после того, как сформируется дерево виртуального дома, и по нему будет обновлено дерево реального дома. Все перечисленное делается в одном синхронном куске кода, без микротасков и т.д. Разумеется, до этапа отрисовки на экране.
Почему снизу вверх, то есть от чилдов к парентам? Значения рефов могут использоваться в колбэках useLayoutEffect, значит они к этому моменту должны быть готовы. Т.е. чилд должен успеть назначить реф до того, как парент попробует к нему обратиться. Распространение данных в обратную сторону, в комплект к распространению в прямую (через пропсы). Это одна из причин, могут быть и другие.
Эффекты useEffect срабатывают после отрисовки на экране, т.е. в отдельном таске. Для единообразия они тоже срабатывают в порядке от чилдов к парентам.
Указанный порядок имеет свои минусы, например
https://habr.com/ru/articles/574536/ , но в целом плюсы перевешивают.
Надо заметить, что и функции очистки для useEffect и useLayoutEffect тоже вызываются сначала на чилдах, что правильно со всех сторон.