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.