remove repeated headers between list items react native

Viewed 71

I'm using @react-native-material and I've got displayed listitems, as well as headers, like this for example

**one**
pink 1
**one**
purple 1
**two**
blue 2
**four**
aqua 4

But I want to try and display it so that the headers aren't repeated like

**one**
pink 1
purple 1
**two**
blue 2
**four**
aqua 4

how could I go around doing that? This is my code. (i removed some code related with apis since it's not necessary here)

const App = () => {
  const [products, setProducts] = useState<ProductType[] | []>([]);
  const [userProducts, setUserProducts] = useState<ProductType[] | []>([]);
  const [value, setValue] = useState(' ');
  const [isFocus, setIsFocus] = useState(false);
  const [visible, setVisible] = useState(false);
  const [productId, setProductId] = useState('');
  const [product, setProduct] = useState('');
  const [num, setNum] = useState('');
  const [amount, setAmount] = useState('');




  React.useEffect(() => {
    getProducts();
    getUserProducts();
    console.log(userProducts);
  }, []);

  return (
    <>
      <Provider>
      

        <ScrollView>
          {userProducts.length > 0 ? (
            userProducts.map(userProduct => (
              <>
                <Text>{userProduct.num}</Text>
                <ListItem
                  title={userProduct.product + ' x' + userProduct.amount}
                  onPress={async () => {
                    await deleteProduct(userProduct.product_id);
                    console.log('deleted');
                    getUserProducts();
                    ToastAndroid.show('Done', ToastAndroid.SHORT);
                  }}
                  trailing={<Text>{userProduct.num}</Text>}
                />
              </>
            ))
          ) : (
            <Text>Nothing in your list yet</Text>
          )}
        </ScrollView>
      </Provider>
    </>
  );
};
export default App;

const styles = StyleSheet.create({
  dropdown: {
    height: 50,
    borderColor: 'gray',
    borderWidth: 0.5,
    borderRadius: 8,
    paddingHorizontal: 8,
  },
  row: {
    flexDirection: 'row',
    flexWrap: 'wrap',
  },
});
3 Answers

Without making changes to the object layout that is in userProducts, you can create a ref variable that will keep track of previous header through each map cycle. For Example:

const prevHeader = useRef('NA')

userProducts.map(userProduct => {
   if(prevHeader.current === userProduct.num){
      return <ListItem ... />
   } else {
      prevHeader.current = userProduct.num
      return (<>
         <Text>{userProduct.num}</Text>
         <ListItem ... />
       <>)
   }
}

The code above should be able to go into the ScrollView tag.

Hope this helps!

One option is to generate a key value pair from userProducts where the keys are userProduct.num and the values are a list of type userProduct. Then, instead of rendering userProducts, render the new key value pair — the headers are keys and you generate a ListItem for every entry in the value array.

you can achieve this with Section List, as shown in docs you will just need to rearrange the data you want to pass to the list. And you can use your ListItem in renderItem like this.

<SectionList
      sections={userProducts}
      keyExtractor={(item, index) => item + index}
      renderItem={({ item }) => 
                <ListItem
                  title={item.product + ' x' + item.amount}
                  onPress={async () => {
                    await deleteProduct(item.product_id);
                    console.log('deleted');
                    getUserProducts();
                    ToastAndroid.show('Done', ToastAndroid.SHORT);
                  }}
                  trailing={<Text>{item.num}</Text>}
                />}
      renderSectionHeader={({ section: { title } }) => (
        <Text>{userProduct.num}</Text>
      )}
    />

if you face any issue with scrolling after adding SectionList, try removing ScrollView

Related