The high-level summary of my problem is that I need to give one row of a FlatList a higher z-index than the rest. There are two factors at play in the internal implementation of
react-native-web that make this task impossible:
- Each view creates its own stacking index.
- It seems like
react-native-webinternally wraps each row with a View somewhere, because in the DOM each row is wrapped in a div.
Therefore, even after giving one row of the FlatList a higher z-index than the rest in
renderItem, that z-index will have no effect, because
react-native-web wraps each row in a view that creates it’s own stacking context.
How to reproduce
Simplified test case: https://codesandbox.io/s/flamboyant-pond-g8k6j?file=/src/App.js
Steps to reproduce:
- Create a
renderItemuses absolute positioning to overlap part of its content over another row.
- Set the
zIndexof the item you want to show on top.
- Notice that the
zIndexhas no effect.
zIndex of a row in a
FlatList should work.
Environment (include versions). Did this work in previous versions?
- React Native for Web (version):
- React (version):
- Browser: Google Chrome
Some background on the problem
I am working on a cross-platform chat app, and we’re using a FlatList component to render the messages in a chat:
I am trying to build a context menu for each message, that appears on hover and looks something like this:
So I implemented this by giving each row an absolute-positioned menu, and displaying it if the row is hovered. So far so good, but there’s a problem where long rows of text will be covered by the menu:
To fix this, I tried to give the menu a negative value for
top to shift it upwards, but it’s covered by another row:
So what I need to do to fix that is give the hovered row (which is currently displaying the menu) a higher z-index than the other rows. That works if I manually adjust the style from the chrome dev console:
So I built this out programmatically, such that the
renderItem function of the FlatList will give the outermost view of each row a z-index of 1, while all others have a default z-index of 0. However, this didn’t solve my problem.
I can include the floating context menu outside the
FlatList and absolute-position it over the row that’s hovered. However, that has a performance impact on the application because it requires that I call
measureLayout in response to scroll events. It also results in the menu “wiggling around”, since there is a delay in between the FlatList re-rendering with its updated scroll position and
measureLayout completing and re-rendering the menu.
I don’t claim to be an expert on the internals of
react-native-web, so I’m sure you’ll have a much better idea of what a solution might look like, but here are three potential suggestions I can think of:
Viewcomponent has a default
0so that each view creates its own stacking context. You could make an exception and give the
Viewcreated for each row within
auto, so that it doesn’t create a stacking context.
- Maybe you could refactor the
FlatListso instead of using a
Viewinternally it uses
- You could offer an extra prop to the
FlatListso that users can specify a stacking order/zIndex for each row by index or something?
One last note:
If you include the
inverted prop on the FlatList, then the overlap works as expected when a row overlaps the next row down, but not for the next row up. If you do not include the
inverted prop on the FlatList, then the overlap works as expected when a row overlaps the next row up, but not the next row down.