Creating a multi-item selector component in React Native

You might have a use case in your application where you need the user to select multiple items, for example, selecting favorite artists from a large list of options. This article aims to help you in creating a component that is highly customizable, reusable, and robust.

Individual button component

We will first create a reusable button component that we will render in our selector component. This component will contain a simple TouchableHighlight that will change its style when it is pressed, indicating its selection status.

We create a state named selected which will store a boolean which will indicate if the button is pressed or not. This state will be toggled at the press of the button and a callback function onPress (which would be accepted as a component prop) will be invoked with the latest selected value. This will help us inform the parent component that an option has been selected.

import React, {useCallback, useEffect, useState} from 'react';
import {View, TouchableHighlight} from 'react-native';

const OptionButton = ({
  id,
  text,
  onPress,
}) => {

  const [selected, setSelected] = useState(false);

  const buttonBackgroundColor = selected
    ? '#1DA1F2'
    : '#FFFFFF00';
    
  const buttonTextColor = selected 
      ? '#FFFFFF' 
      : '#1DA1F2';

  const buttonStyle = {
    ...localStyle.preferenceButton,
    backgroundColor: buttonBackgroundColor,
  };

  const buttonTextStyle = {
      ...localStyle.preferenceButtonText, 
      color: buttonTextColor
  };

  const onSelect = useCallback(() => {
    setSelected(prevState => !prevState);
    onPress?.(id, !selected);
  }, [id, onPress, selected]);

  return (
    <View>
      <TouchableHighlight
        text={text}
        style={buttonStyle}
        textStyle={buttonTextStyle}
        onPress={onSelect}
        underlayColor='#65898D33'
      />
    </View>
  );
};

export default React.memo(OptionButton);

const localStyle = {
  preferenceButton: {
    alignSelf: 'center',
    justifyContent: 'center',
    alignItems: 'center',
    width: 'auto',
    height: 30,
    borderRadius: 30,
    borderWidth: 1,
    paddingHorizontal: 8,
    paddingVertical: 5,
    marginVertical: 5,
    marginRight: 10,
    borderColor: '#1DA1F2'
  },
  preferenceButtonText: {
    textTransform: 'uppercase',
    fontSize: 10,
    lineHeight: 14,
    alignItems: 'center',
    letterSpacing: 0.8,
    color: '#003c43',
  },
};

This will result in a component that will look like this

Selector component

We will now create the selector component that should render multiple OptionButtons from an array of data. Let's create the UI for rendering the buttons in a list first.


//Getting these values from a custom hook defined below.
const {aData, fnOnItemSelect, fnOnSearchInput} =
useItemSelectorData(props);

return (
<View>
  <View style={localStyle.searchBarContainer}>
    <TextInput
      cursorColor='#1DA1F2'
      style={localStyle.searchBar}
      placeholder="Search topics"
      onChangeText={fnOnSearchInput}
    />
  </View>

  <ScrollView
    showsVerticalScrollIndicator={false}
    contentContainerStyle={localStyle.optionsContainer}>
    {aData.map(item => (
      <OptionButton
        text={item.title}
        id={item.id}
        key={item.id}
        onPress={fnOnItemSelect}
        isSelected={item.isSelected}
      />
    ))}
  </ScrollView>
</View>
);

The above code will give us a UI that should look something like this

Next, we will move on to the implementation of the logic and filtering of these items.

function useItemSelectorData() {

    //Getting an array of items from the database.
    const originalDataArray = getItemsData();

    const [data, setData] = useState(originalDataArray);

    const [selectedItems, setSelectedItems] = useState([]);

    const onItemSelect = useCallback(
        (id, isSelected) => {

          //Creating a copy of the selected items array.
          const arr = [...selectedItems];

          if (isSelected) {
            //If the item is selected, we push it in the array.
            arr.push(id);
          } else {
            //If the item is deselected, we remove it from the array.
            arr.splice(arr.indexOf(id), 1);
          }

          //Setting the item array with with the updated array.
          setSelectedItems([...arr]);
        },
        [selectedItems],
    );
...

In the method onItemSelect, we toggle the addition and subtraction of an item from the selectedItems array when an item is pressed. This would result in the following behavior.

Next, we will filter the list depending on the search term entered in the text field.

...
    const onSearchInput = useCallback(
        searchText => {
          if (searchText === '') {

            setData([...originalDataArray]);

          } else {

            let filteredData = originalDataArray.filter(item => {
              return item.title.includes(searchText);
            });

            setData([...filteredData]);

          }
        },
        [originalDataArray],
    );

    return {
        aData: data,
        fnOnSearchInput: onSearchInput,
        fnOnItemSelect: onItemSelect
    }
}

onSearchInput method manipulates the data array depending on the search term entered.

Conclusion

This article focuses on creating a multi-item selector component in react native efficiently. This component is highly customizable and can adapt to your requirements.

Harsh Siriah

Harsh Siriah

Hi, I'm Harsh! A geek with a love for dogs & mobiles.
Pune, India