admin管理员组

文章数量:1356413

I'm trying to implement helper ponents 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 ponents are added in the "middle" of the ponent 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 ponents 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 ponent tree. If they were direct child elements, I'd guess it would be easy - in my case though I have nested ponents which can output rows.

I'm trying to implement helper ponents 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 ponents are added in the "middle" of the ponent 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 ponents 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 ponent tree. If they were direct child elements, I'd guess it would be easy - in my case though I have nested ponents which can output rows.

Share Improve this question edited Jun 12, 2018 at 7:03 Derek Pollard 7,1756 gold badges43 silver badges60 bronze badges asked Jun 9, 2018 at 17:41 Kieren JohnstoneKieren Johnstone 42k16 gold badges97 silver badges149 bronze badges 11
  • Edit: have tried doing a recursive map using something like stackoverflow./a/42498730/376409, but that doesn't look to work with redux-connect()-ed ponents, i.e. it won't recurse into them :'( – Kieren Johnstone Commented Jun 10, 2018 at 13:38
  • how are rows rendered – Shubham Khatri Commented Jun 12, 2018 at 7:09
  • Could you elaborate on that question please @ShubhamKhatri ? The example above shows how I'd like the Row ponent to be used – Kieren Johnstone Commented Jun 12, 2018 at 15:07
  • 1 It looks like your problem could related to React reconcilation. Have you tried to use React's key ? – An Nguyen Commented Jun 13, 2018 at 6:03
  • @FiriceNguyen I'm not sure I can see what you mean. There's no mapping/array and so I can't see how a key would e in to it? – Kieren Johnstone Commented Jun 13, 2018 at 6:19
 |  Show 6 more ments

2 Answers 2

Reset to default 2

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

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

The container's ponentDidUpdate checks the re-calc flag and pares all of the DOM notes (via the refs) using the browser DOM function pareDocumentPosition. 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.

It seems that you rely on render() to provide you with the recursive structure inside your DOM Tree. The reason it works "the first time" is that all the ponents need to be rendered the first time they are presented to react.

However, subsequent render() calls on ColumnContainer do not imply renders on the sub-ponents because their state or props might have not changed.

I think you should try the following:

  1. Change your ColumnContainer.Row registration to the ColumnContainer array in a way that the children rembember rowIdx in their state on the first call and then reuse this value for subsequent render calls.

(so to do this effectively you would need:)

  1. Change your registration procedure in two function calls (these two functions should be in your context):

    • a function that gets the rowIdx for a child and remembers it. (this function is only called if you don't have a cached value for rowIdx)
    • a second function that does the work but does not need rowIdx as a parameter. (this function is called just like now, every time).

本文标签: javascriptReact Get component order within hierarchyStack Overflow