ifelsething logoifelsething

Cell Separator in VirtualizedList

Published On: 2024-07-16
Posted By: Harish

separator between virtualizedlist items

Sometimes we may need to add a different view in a list. We cannot use this view in the list as this will behave as a list item. Let's say we want to add a line in between two list items..

We can use contentContainerStyles prop to add margin or padding to the content cells but not a line. For that we have to add a bottom line to the cell view. But this will make us write extra code to remove that line for the bottom item. This can become more work when another view component is used other than a line.

So, to avoid that hectic work, we have a callback called ItemSeparatorComponent which will render a component in between two list items ignoring top and bottom places of the list.

Let's check the working of this separator in action.

Create A New Project

Create a new react-native project by using npx. Check documentation for creating a new react native project.

npx react-native@latest init VListRN

Example Implementation

We will create a simple colorful vertical VirtualizedList and add a black line in between color blocks.

Import and add VirtualizedList. Add basic VirtualizedList with color blocks. Check basic VirtualizedList implementation for more info.

//App.tsx
...
import { VirtualizedList } from 'react-native';
...
<VirtualizedList
  contentContainerStyle={styles.content_container}
  data={colors}
  getItemCount={getItemCount}
  getItem={getItem}
  keyExtractor={item => item.id}
  renderItem={({ item }) => {
    return (
      <Item color={item.color} />
    )
  }}
/>
...

Run the app,

#for Android
npx react-native run-android

#for ios
npx react-native run-ios

You will see a few colored blocks.

normal color blocks in virtualizedlist

Now, we need to add a black separator in between the items.

So, create a new component for the black line, add ItemSeparatorComponent to the VirtualizedList and use that newly created component to return in this callback.

...
const LineSeparator = () => {
  return (
    <View style={styles.separator} />
  )
};
...
<VirtualizedList
  ...
  ItemSeparatorComponent={() => {
    return <LineSeparator />
  }}
/>
...

If you re-run the app, you will see a black line in between color blocks.

row separator of virtualizedlist items

Using this ItemSeparatorComponent function, we can implement conditional rendering of this separator. This function receives props related to the latest rendered item.

Add props to the separator component and console log it. You will see logs similar to below logs.

LOG  {"highlighted": false, "leadingItem": {"color": "orange", "id": "0.892a7a57495773b"}}
LOG  {"highlighted": false, "leadingItem": {"color": "green", "id": "0.917769b1694ba05"}}
...

Lets say, we want a black line in between darkorange and gold blocks.

For that, get the leadingItem's color and write a condition to apply line only when leading item is darkorange

...
type ItemSeparatorProp = {
  highlighted: boolean,
  leadingItem: ItemDataProp
}
...
const LineSeparator = ({ itemData }: { itemData: ItemSeparatorProp }) => {
  const { color } = itemData.leadingItem;
  return (
    color === 'darkorange'
      ? <View style={styles.separator} />
      : null
  )
};
...
<VirtualizedList
  ...
  ItemSeparatorComponent={(props) => {
    return <LineSeparator itemData={props} />
  }}
/>
...

If you rebuild the metro, you will see a black line in between dark orange and gold blocks only.

conditional row separator in virtualizedlist

Complete code of our example,

//App.tsx
import React from "react";
import {
  Text,
  StyleSheet,
  SafeAreaView,
  StatusBar,
  View,
  VirtualizedList,
} from "react-native";

const colors = [
  'orange',
  'green',
  'blue',
  'maroon',
  'violet',
  'darkorange',
  'gold',
  'darkgreen',
  'aquamarine',
  'cadetblue'
];

type ItemProp = {
  color: string;
};

type ItemDataProp = {
  id: string;
  color: string;
};

type ItemSeparatorProp = {
  highlighted: boolean,
  leadingItem: ItemDataProp
}

export default function App() {
  const Item = (props: ItemProp) => {
    const { color } = props;
    return (
      <View
        key={color}
        style={[
          styles.view,
          {
            backgroundColor: color
          }
        ]}
      />
    )
  };
  const getItem = (data: string[], index: number) => ({
    id: Math.random().toString(12).substring(0),
    color: data[index],
  } as ItemDataProp);
  const getItemCount = (data: string[]) => data.length;
  const LineSeparator = ({ itemData }: { itemData: ItemSeparatorProp }) => {
    const { color } = itemData.leadingItem;
    return (
      color === 'darkorange'
        ? <View style={styles.separator} />
        : null
    )
  };
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: 'white' }}>
      <StatusBar
        barStyle="dark-content"
      />
      <View style={styles.container}>
        <Text style={styles.text}>
          ifelsething.com
        </Text>
        <Text style={styles.text}>
          row separator in VirtualizedList
        </Text>
        <VirtualizedList
          contentContainerStyle={styles.content_container}
          data={colors}
          getItemCount={getItemCount}
          getItem={getItem}
          keyExtractor={item => item?.id}
          ItemSeparatorComponent={(props) => {
            return <LineSeparator itemData={props} />
          }}
          renderItem={({ item }) => {
            return (
              <Item color={item.color} />
            )
          }}
        />
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    margin: 10,
    gap: 20
  },
  text: {
    fontSize: 15,
    color: 'black',
    fontStyle: 'italic'
  },
  content_container: {
    gap: 10
  },
  view: {
    flexShrink: 1,
    width: '100%',
    height: 200,
    borderRadius: 10,
    alignSelf: 'center'
  },
  separator: {
    marginTop: 10,
    height: 4,
    backgroundColor: 'black',
    borderRadius: 10
  }
});

Share is Caring

Related Posts