Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: Get component order within hierarchy

I'm trying to implement helper components for CSS Grids. I have something like (prepare yourself):

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

ColumnContainer:

  • Is a container div with display: grid and various grid properties set up
  • Is also a context provider

Then, ColumnContainer.Row:

  • Is basically a context consumer
  • Takes a function as a child
  • Doesn't need to be a direct child of ColumnContainer - hence using context

The context provided is an integer, layoutVersion, which is incremented whenever the set of rows is intended to change (to trigger a re-render), and - hack of hacks - an empty array.

The idea is, as each ColumnContainer.Row renders, it adds itself (could be any object) to the array whose reference is passed in the context, and renders the child function with the size of the array as the parameter (row index).

Believe it or not, this works, for the first render and if rows are just added to the end.

However, when components are added in the "middle" of the component DOM, the resulting rendered rows are out-of-order (but not overlapping). Meaning, I think, that in the case of a new layout version (re-render), all the ColumnContainer.Rows are re-rendered, but not necessarily in the 'natural' order they are in, i.e. in the DOM.

My guess is that depending on components to have render() called in a certain order is a bad idea, as well as modifying the contents of context properties in render().

What are my other options - what I really want is to know the 'natural' order of descendent nodes within a component tree. If they were direct child elements, I'd guess it would be easy - in my case though I have nested components which can output rows.

like image 292
Kieren Johnstone Avatar asked Jun 09 '18 17:06

Kieren Johnstone


1 Answers

I have found a dodgy/hacky solution, by having child components 'register' themselves (a unique ID and ref) with the container component via a callback injected in the context.

The container component accumulates these callbacks using functional state updates and setting a flag in the state that a re-calc is required.

The container's componentDidUpdate checks the re-calc flag and compares all of the DOM notes (via the refs) using the browser DOM function compareDocumentPosition. The next state update clears the re-calc flag and includes the order of each of the children's IDs.

This state is then passed down to all children via context, where they can look up their index by ID.

Clearly that sucks but it's all I have for now. Would still like to award bounty to a better solution.

like image 106
Kieren Johnstone Avatar answered Nov 13 '22 17:11

Kieren Johnstone