react-native icon indicating copy to clipboard operation
react-native copied to clipboard

zIndex in a List

Open TheNoim opened this issue 6 years ago • 30 comments

This is actually a reopen of my #16878 issue. I've got some thumbs up, so I think I am not the only one with this issue. This time I updated my example project to a pure rn project with the latest release (rn Branche)

Environment

info
  React Native Environment Info:
    System:
      OS: macOS High Sierra 10.13.6
      CPU: (8) x64 Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
      Memory: 94.53 MB / 16.00 GB
      Shell: 5.3 - /bin/zsh
    Binaries:
      Node: 10.12.0 - ~/.nvm/versions/node/v10.12.0/bin/node
      Yarn: 1.16.0 - ~/.nvm/versions/node/v10.12.0/bin/yarn
      npm: 6.9.0 - /usr/local/bin/npm
      Watchman: 4.9.0 - /usr/local/bin/watchman
    SDKs:
      iOS SDK:
        Platforms: iOS 12.1, macOS 10.14, tvOS 12.1, watchOS 5.1
      Android SDK:
        API Levels: 23
        Build Tools: 23.0.1
    IDEs:
      Xcode: 10.1/10B61 - /usr/bin/xcodebuild
    npmPackages:
      react: 16.8.3 => 16.8.3
      react-native: 0.59.8 => 0.59.8
    npmGlobalPackages:
      react-native-cli: 2.0.1
      react-native-git-upgrade: 0.2.7

Steps to Reproduce

  1. Make a list (FlatList or Array.map, it happens with both)
  2. The renderItem should get an onPress event
  3. The onPress event changes the style to a high zIndex and position absolute

Expected Behavior

I expect that the component with the high zIndex is over every other component.

Actual Behavior

It seems like it ignores the zIndex property. The behavior is exactly like I would not apply any zIndex. I can remove the zIndex and I get the same result. Google Drive Link to a screenshot.

Reproducible Demo

Github Demo Project rn Branche This is just an extract out of my current rn project. Expo Link (Not on the latest rn version) Google Drive Video which show the issue

TheNoim avatar Mar 29 '18 09:03 TheNoim

I was about to open a proposal on this, but might be better to just discuss in this issue.

I came across the same problem and worked around it by adding an additional cellStyle prop to my FlatList component. I then added this cellStyle prop to the styles array generated by CellRenderer in VirtualizedList.

Setting the zIndex to the parent element generated by CellRenderer works as you'd expect.

Happy to put in a PR if this seems like an acceptable solution.

@TheNoim For the time being you could use patch-package to make the same change in this commit: https://github.com/mattmcdonald-uk/react-native/commit/a59888b9b68698985b784b6ee29a80c9b878e28d

mattmcdonald-uk avatar Mar 29 '18 13:03 mattmcdonald-uk

@mattmcdonald-uk Thanks for the solution, but I can't use it. I tried to modify your attempt (mattmcdonald-uk/react-native@a59888b). In my example repo I use a wrapper component which can't have the cellStyle prop and I tried to access the applied style with other methods, but I failed. It is a workaround but not a final solution. This should really get fixed.

Off topic: react-native-bot 1, @hramos 0 :D Just kidding.

TheNoim avatar Mar 29 '18 18:03 TheNoim

Having the same issue here

ethanyuwang avatar Apr 11 '18 00:04 ethanyuwang

Will we ever get a fix for this? I mean, this is the second issue about this and still: nothing

TheNoim avatar Apr 27 '18 05:04 TheNoim

As far as I can tell, no PRs have been submitted to address this issue. I'm adding the Help Wanted tag to surface the issue more broadly.

hramos avatar May 10 '18 22:05 hramos

Thanks for posting this! It looks like your issue may refer to an older version of React Native. Can you reproduce the issue on the latest release, v0.55?

Thank you for your contributions.

react-native-bot avatar May 15 '18 20:05 react-native-bot

No need to override any React Native internals to set zIndex on the item wrapper views of a FlatList, here’s how I achieved it;

<FlatList
    data={timeline}
    renderItem={this.getItemView}
    CellRendererComponent={({ children, index, style, ...props }) => {
        const cellStyle = [
            style,

            // I want each item to have a higher zIndex than the previous one,
            // in reversed order due to the FlatList being inverted
            { zIndex: this.state.timeline.length - index }
        ]
		
        // OverflowableView for Android...
        return (
            <OverflowableView style={cellStyle} index={index} {...props}>
                {children}
            </OverflowableView>
        )
    }}
    inverted />

Haraldson avatar May 16 '18 08:05 Haraldson

@Haraldson It could solve my exact case (Not tested), but the not the core problem with zIndex in a List. We can consider this as an workaround. @react-native-bot Still there in v0.55. Source

TheNoim avatar May 21 '18 18:05 TheNoim

@Haraldson That workaround looks reasonable, but unfortunately when I override CellRendererComponent, handling of keys seems to break, even if I pass the same key to the first child returned in CellRendererComponent. Any advice there?

isaachinman avatar Oct 05 '18 10:10 isaachinman

@isaachinman I don’t quite get the problem? key should be part of props, which is spread into the Cell view.

Haraldson avatar Oct 05 '18 11:10 Haraldson

@Haraldson Let me explain.

Generally speaking, I am attempting to animate the reorder of items within a FlatList using LayoutAnimation. Here is the simplest possible example of that, working just fine.

The problem I've run into is that depending on the direction an item is moving (up or down), it will animate behind the other items, as it is after them in the literal JSX order.

So, I want to increase the zIndex/elevation of an item just when it is animating. Which is how I arrived at this thread. Here's the same example, but using the most basic version of your CellRendererComponent example. The animation is now broken, despite the keys being present and remaining identical.

Even if I only return children directly, the animation breaks due to full re-renders of everything.

Hopefully this is clear... I have no idea what's going on here.

isaachinman avatar Oct 05 '18 14:10 isaachinman

I think this issue should definitely get fixed. A workaround would be ok, but I would prefer something by the core team of rn.

TheNoim avatar Oct 05 '18 15:10 TheNoim

Hello there 👋 this issue seems to have been inactive for the past few months. Because of this, it's likely that the issue is not a high priority anymore or it has been solved by OP; for these reasons, we'll close it.

But please, if it's actually still an issue with 0.59 please comment below and we can reopen it or please send us a Pull Request with a fix 😊

kelset avatar May 16 '19 10:05 kelset

@kelset Issue still persists.

TheNoim avatar May 25 '19 22:05 TheNoim

Can you submit a repro?

kelset avatar May 27 '19 12:05 kelset

I did. In the initial issue. I updated the repo.

TheNoim avatar May 27 '19 12:05 TheNoim

The one submitted above is using RN 0.54 (https://github.com/TheNoim/zIndexBug/blob/3e36101903ab96841ba4841e6db214cb4a7df50e/package.json#L18)- as I mentioned when I closed the issue, we'd need one with 0.59

kelset avatar May 27 '19 12:05 kelset

went through the hassle and updated to 0.59

TheNoim avatar May 27 '19 13:05 TheNoim

@kelset I proved, it is still an issue. Please reopen it again.

TheNoim avatar Jun 06 '19 16:06 TheNoim

sorry, I've missed the other comment. Reopening.

kelset avatar Jun 07 '19 10:06 kelset

@kelset Hello. Could you tell me please, what we can do with issue? I`m trying to make this case in android and I have bad result - the ScrollView is under it.... ()

ghost avatar Aug 04 '19 01:08 ghost

@KassoBrapik Not sure if this is the same issue.

TheNoim avatar Aug 11 '19 10:08 TheNoim

@TheNoim is there any way to position fix element inside ScrollView ?

adnandothussain avatar Aug 16 '19 16:08 adnandothussain

I am experiencing the following issue only with android. View disappears when rendered inside a FlatList and have position: 'absolute'

In the example below the Locations component renders a FlatList

class Locations extends Component {
  renderCard(item, index) {
    return (
      <Location location={item} />
    )
  }

  render() {
    const { locations } = this.state
    return (
      <FlatList
        data={locations}
        renderItem={({ item, index }) => this.renderCard(item, index)}
      />
    )
  }
}

The Location Component renders a View

class Location extends Component {
   render() {
        <View style={styles.container}>
          <Text>Test</Text>
        </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: 'red',
    position: 'absolute',
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
  }
})

If I remove flatlist the Red View with the Text on top is displayed. If I add the flatlist, the Red View and text element are not displayed.

I was able to avoid this issue by wrapping the view inside a container view like this:

class Location extends Component {
   render() {
        <View style={{height: 100, width: 100}}>
          <View style={styles.container}>
            <Text>Test</Text>
          </View>
       </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: 'red',
    position: 'absolute',
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
  }
})

fabOnReact avatar Oct 21 '19 07:10 fabOnReact

the example is based on rn 0.54.3 https://github.com/TheNoim/zIndexBug/blob/rn/package.json#L18

There is another example based on rn 0.59 https://github.com/TheNoim/zIndexBug/blob/master/package.json#L12

I am not sure if the original issue reproduces on latest react-native version and on android, there are several workaround for fixing zIndex issues and they can be adopted in this use case.

fabOnReact avatar Apr 28 '20 13:04 fabOnReact

I tried to test the above issue in the react native master branch. During the implementation of the example I noticed that @TheNoim added dependencies like 'react-native-material-cards' that are not part of the react-native library. I believe that reproducible examples should not include dependencies other then react-native...

fabOnReact avatar May 18 '20 08:05 fabOnReact

No need to override any React Native internals to set zIndex on the item wrapper views of a FlatList, here’s how I achieved it;

<FlatList
    data={timeline}
    renderItem={this.getItemView}
    CellRendererComponent={({ children, index, style, ...props }) => {
        const cellStyle = [
            style,

            // I want each item to have a higher zIndex than the previous one,
            // in reversed order due to the FlatList being inverted
            { zIndex: this.state.timeline.length - index }
        ]
		
        // OverflowableView for Android...
        return (
            <OverflowableView style={cellStyle} index={index} {...props}>
                {children}
            </OverflowableView>
        )
    }}
    inverted />

This particular code causes a crash on android react native 62.2... So its no longer a valid workaround.

CarbonC avatar Jun 02 '20 06:06 CarbonC

No need to override any React Native internals to set zIndex on the item wrapper views of a FlatList, here’s how I achieved it;

<FlatList
    data={timeline}
    renderItem={this.getItemView}
    CellRendererComponent={({ children, index, style, ...props }) => {
        const cellStyle = [
            style,

            // I want each item to have a higher zIndex than the previous one,
            // in reversed order due to the FlatList being inverted
            { zIndex: this.state.timeline.length - index }
        ]
		
        // OverflowableView for Android...
        return (
            <OverflowableView style={cellStyle} index={index} {...props}>
                {children}
            </OverflowableView>
        )
    }}
    inverted />

This workaround worked as I expected, but when I do this onEndReached is not called, so FlatList doesn't load more data. Anyone having this problem?

Here is my code:

const CellRendererComponent = (props: any) => {
  const { children } = props
  const [isSwipingCell, setIsSwipingCell] = useState(false)

  const onSwipeStart = () => {
    setIsSwipingCell(true)
  }

  const onSwipeEnd = () => {
    setIsSwipingCell(false)
  }

  return (
    <View
      style={{
        zIndex: isSwipingCell ? 100 : 0,
        flexDirection: 'row',
        width: size.screenWidth,
      }}>
      {React.cloneElement(children[0].props.children[0].props.children, {
        onSwipeStart: onSwipeStart,
        onSwipeEnd: onSwipeEnd,
      })}
      {children[0].props.children[1] &&
        React.cloneElement(children[0].props.children[1].props.children, {
          onSwipeStart: onSwipeStart,
          onSwipeEnd: onSwipeEnd,
        })}
    </View>
  )
}

export default function LikeList() {
  const [data, setData] = useState(DATA)

  const onSwipeSuccess = (id: string) => {
    setData(_data => _data.filter(item => item.id !== id))
  }

  const renderItem = useCallback(({ index, item }) => {
    return (
      <MovingCard
        index={index}
        data={item}
        onSwipeSuccess={onSwipeSuccess}
        onSwipeStart={undefined}
        onSwipeEnd={undefined}
      />
    )
  }, [])

  const keyExtractor = useCallback(item => {
    return item.id
  }, [])

  return (
    <View
      style={{
        flex: 1,
        backgroundColor: color.background.primary,
      }}>
      <FlatList
        data={data}
        CellRendererComponent={CellRendererComponent}
        renderItem={renderItem}
        keyExtractor={keyExtractor}
        numColumns={2}
        horizontal={false}
      />
    </View>
  )
}

seunghyun-woo avatar Jun 04 '20 09:06 seunghyun-woo

Couldn't make this workaround work on Android without also having removeClippedSubviews={false} explicitly set.

It is set to true by default on Android, and false on iOS.

yoannmoinet avatar Jan 03 '21 14:01 yoannmoinet

Couldn't make this workaround work on Android without also having removeClippedSubviews={false} explicitly set.

It is set to true by default on Android, and false on iOS.

It's working, thank you.

bolan9999 avatar Oct 18 '22 01:10 bolan9999