diff --git a/README.md b/README.md index 324d56c1..e2458817 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,23 @@ Multiple Select List | Select List
+# What's New in This Fork (changes made for selectList ONLY) + +This fork by **FearZaEngineer** is an edit of the original package and introduces key performance optimizations and improvements: + +- **Optimized Rendering with FlatList:** + Replaces the ScrollView in the dropdown with a FlatList for virtualized rendering. This ensures that only visible items are rendered, improving performance with large data sets. +- **New Prop: `flatListProps`:** + Allows you to pass additional props directly to the underlying FlatList. This gives you full control over performance-related configurations (e.g., `initialNumToRender`, `maxToRenderPerBatch`, `windowSize`, etc.). +- **Memoized Render Functions:** + Uses `useCallback` and `useMemo` to memoize render functions and helper functions, reducing unnecessary re-renders. +- **Enhanced Search Filtering:** + Implements local state for search queries, ensuring smoother and more efficient filtering. +- **Animation Improvements:** + The slide-up/slide-down animations have been optimized to perform better during state changes. + +
+ # Compatibility
@@ -48,14 +65,16 @@ Multiple Select List | Select List # 🔌 Installation ```sh -$ npm install react-native-dropdown-select-list +$ npm install react-native-dropdown-select-list@github:FearZaEngineer/react-native-dropdown-select-list#my-custom-selectlist + ``` OR ```sh -$ yarn add react-native-dropdown-select-list +$ yarn add react-native-dropdown-select-list@github:FearZaEngineer/react-native-dropdown-select-list#my-custom-selectlist + ```
@@ -148,6 +167,8 @@ For Live `Demo` [(Expo Snack)](https://snack.expo.dev/@danish1658/react-native-d | fontFamily| string | Pass font name to apply globally on each text field of component | notFoundText| string | Pass your custom message if any search result returns empty | dropdownShown| boolean | Control your dropdown ( on & off ) state by changing its value to true or false +| flatListProps| object | NEW : Additional props for item List +
@@ -264,9 +285,7 @@ const App = () => {
-# 💲 Would you like to support me? - -If you would like me come up with similar packages, buy me a cup of coffee to boost my energy. +# 💲 Would you like to support the creator of the original package?

[![Paypal](https://www.paypalobjects.com/webstatic/mktg/Logo/pp-logo-100px.png)](https://paypal.me/danishamindar)

diff --git a/components/SelectList.tsx b/components/SelectList.tsx index 234a7e10..69a8a6ba 100644 --- a/components/SelectList.tsx +++ b/components/SelectList.tsx @@ -1,263 +1,286 @@ -import React from 'react'; +import React, { useCallback, useEffect, useRef, useState, useMemo } from 'react'; import { - View, - Text, - StyleSheet, - Image, - TouchableOpacity, - ScrollView, - Animated, - TextInput, - Keyboard + View, + Text, + StyleSheet, + Image, + TouchableOpacity, + Animated, + TextInput, + Keyboard, + FlatList } from 'react-native'; import { SelectListProps } from '..'; -type L1Keys = { key?: any; value?: any; disabled?: boolean | undefined } +type L1Keys = { key?: any; value?: any; disabled?: boolean }; -const SelectList: React.FC = ({ - setSelected, - placeholder, - boxStyles, - inputStyles, - dropdownStyles, - dropdownItemStyles, - dropdownTextStyles, - maxHeight, - data, - defaultOption, - searchicon = false, - arrowicon = false, - closeicon = false, - search = true, - searchPlaceholder = "search", - notFoundText = "No data found", - disabledItemStyles, - disabledTextStyles, - onSelect = () => {}, - save = 'key', - dropdownShown = false, - fontFamily - }) => { +const SelectList: React.FC = ({ + setSelected, + placeholder, + boxStyles, + inputStyles, + dropdownStyles, + dropdownItemStyles, + dropdownTextStyles, + maxHeight, + data, + defaultOption, + searchicon = false, + arrowicon = false, + closeicon = false, + search = true, + searchPlaceholder = "search", + notFoundText = "No data found", + disabledItemStyles, + disabledTextStyles, + onSelect = () => {}, + save = 'key', + dropdownShown = false, + fontFamily, + flatListProps = {} - const oldOption = React.useRef(null) - const [_firstRender,_setFirstRender] = React.useState(true); - const [dropdown, setDropdown] = React.useState(dropdownShown); - const [selectedval, setSelectedVal] = React.useState(""); - const [height,setHeight] = React.useState(200) - const animatedvalue = React.useRef(new Animated.Value(0)).current; - const [filtereddata,setFilteredData] = React.useState(data) +}) => { + const oldOption = useRef(null); + const [_firstRender, _setFirstRender] = useState(true); + const [dropdown, setDropdown] = useState(dropdownShown); + const [selectedVal, setSelectedVal] = useState(""); + const [height, setHeight] = useState(200); + const animatedValue = useRef(new Animated.Value(0)).current; + const [filteredData, setFilteredData] = useState(data); + const [searchQuery, setSearchQuery] = useState(""); + const slidedown = useCallback(() => { + setDropdown(true); + Animated.timing(animatedValue, { + toValue: height, + duration: 500, + useNativeDriver: false, + }).start(); + }, [animatedValue, height]); - const slidedown = () => { - setDropdown(true) - Animated.timing(animatedvalue,{ - toValue:height, - duration:500, - useNativeDriver:false, - - }).start() - } - const slideup = () => { - - Animated.timing(animatedvalue,{ - toValue:0, - duration:500, - useNativeDriver:false, - - }).start(() => setDropdown(false)) - } - - React.useEffect( () => { - if(maxHeight) - setHeight(maxHeight) - },[maxHeight]) + const slideup = useCallback(() => { + Animated.timing(animatedValue, { + toValue: 0, + duration: 500, + useNativeDriver: false, + }).start(() => setDropdown(false)); + }, [animatedValue]); - - React.useEffect(() => { - setFilteredData(data); - },[data]) + useEffect(() => { + if (maxHeight) setHeight(maxHeight); + }, [maxHeight]); + useEffect(() => { + setFilteredData(data); + }, [data]); - React.useEffect(() => { - if(_firstRender){ - _setFirstRender(false); - return; - } - onSelect() - },[selectedval]) - + // Filter data based on the search query. + useEffect(() => { + if (searchQuery === "") { + setFilteredData(data); + } else { + const result = data.filter((item: L1Keys) => { + const row = item.value.toLowerCase(); + return row.indexOf(searchQuery.toLowerCase()) > -1; + }); + setFilteredData(result); + } + }, [searchQuery, data]); - React.useEffect(() => { - if(!_firstRender && defaultOption && oldOption.current != defaultOption.key ){ - // oldOption.current != null - oldOption.current = defaultOption.key - setSelected(defaultOption.key); - setSelectedVal(defaultOption.value); - } - if(defaultOption && _firstRender && defaultOption.key != undefined){ - - oldOption.current = defaultOption.key - setSelected(defaultOption.key); - setSelectedVal(defaultOption.value); - } - - },[defaultOption]) + useEffect(() => { + if (_firstRender) { + _setFirstRender(false); + return; + } + onSelect(); + }, [selectedVal]); - React.useEffect(() => { - if(!_firstRender){ - if(dropdownShown) - slidedown(); - else - slideup(); - - } - - },[dropdownShown]) + useEffect(() => { + if (!_firstRender && defaultOption && oldOption.current !== defaultOption.key) { + oldOption.current = defaultOption.key; + setSelected(defaultOption.key); + setSelectedVal(defaultOption.value); + } + if (defaultOption && _firstRender && defaultOption.key !== undefined) { + oldOption.current = defaultOption.key; + setSelected(defaultOption.key); + setSelectedVal(defaultOption.value); + } + }, [defaultOption]); + useEffect(() => { + if (!_firstRender) { + if (dropdownShown) slidedown(); + else slideup(); + } + }, [dropdownShown, _firstRender, slidedown, slideup]); + const handleSelectItem = useCallback((item: L1Keys) => { + const key = item.key ?? item.value ?? item; + const value = item.value ?? item; + if (save === 'value') { + setSelected(value); + } else { + setSelected(key); + } + setSelectedVal(value); + slideup(); + setTimeout(() => { + setFilteredData(data); + setSearchQuery(""); + }, 800); + }, [data, save, slideup, setSelected]); - return( - - { - (dropdown && search) - ? - - - { - (!searchicon) - ? - - : - searchicon - } - - { - let result = data.filter((item: L1Keys) => { - val.toLowerCase(); - let row = item.value.toLowerCase() - return row.search(val.toLowerCase()) > -1; - }); - setFilteredData(result) - }} - style={[{padding:0,height:20,flex:1,fontFamily},inputStyles]} - /> - slideup()} > + const renderItem = useCallback(({ item }: { item: L1Keys; index: number }) => { + const key = item.key ?? item.value ?? item; + const value = item.value ?? item; + const disabled = item.disabled ?? false; + if (disabled) { + return ( + {}} + > + + {value} + + + ); + } + return ( + handleSelectItem(item)} + > + {value} + + ); + }, [disabledItemStyles, disabledTextStyles, dropdownItemStyles, dropdownTextStyles, fontFamily, handleSelectItem]); - { - (!closeicon) - ? - - : - closeicon - } - - - - - - - - : - { if(!dropdown){ Keyboard.dismiss(); slidedown() }else{ slideup() } }}> - { (selectedval == "") ? (placeholder) ? placeholder : 'Select option' : selectedval } - { - (!arrowicon) - ? - - : - arrowicon - } - - - } - - { - (dropdown) - ? - - + const keyExtractor = useCallback((item: L1Keys, index: number) => { + return item.key ? String(item.key) : String(index); + }, []); - { - (filtereddata.length >= 1) - ? - filtereddata.map((item: L1Keys,index: number) => { - let key = item.key ?? item.value ?? item; - let value = item.value ?? item; - let disabled = item.disabled ?? false; - if(disabled){ - return( - {}}> - {value} - - ) - }else{ - return( - { - if(save === 'value'){ - setSelected(value); - }else{ - setSelected(key) - } - - setSelectedVal(value) - slideup() - setTimeout(() => {setFilteredData(data)}, 800) - - }}> - {value} - - ) - } - - }) - : - { - setSelected(undefined) - setSelectedVal("") - slideup() - setTimeout(() => setFilteredData(data), 800) - - }}> - {notFoundText} - - } - - - - - - : - null - } - - + return ( + + {dropdown && search ? ( + + + { !searchicon ? ( + + ) : searchicon } + setSearchQuery(val)} + value={searchQuery} + style={[{ padding: 0, height: 20, flex: 1, fontFamily }, inputStyles]} + /> + + { !closeicon ? ( + + ) : closeicon } + + - ) -} + ) : ( + { + if (!dropdown) { + Keyboard.dismiss(); + slidedown(); + } else { + slideup(); + } + }} + > + + {selectedVal === "" ? (placeholder ? placeholder : 'Select option') : selectedVal} + + { !arrowicon ? ( + + ) : arrowicon } + + )} + {dropdown && ( + + + {filteredData.length === 0 && ( + { + setSelected(undefined); + setSelectedVal(""); + slideup(); + setTimeout(() => { + setFilteredData(data); + setSearchQuery(""); + }, 800); + }} + > + {notFoundText} + + )} + + )} + + ); +}; export default SelectList; - const styles = StyleSheet.create({ - wrapper:{ borderWidth:1,borderRadius:10,borderColor:'gray',paddingHorizontal:20,paddingVertical:12,flexDirection:'row',justifyContent:'space-between' }, - dropdown:{ borderWidth:1,borderRadius:10,borderColor:'gray',marginTop:10,overflow:'hidden'}, - option:{ paddingHorizontal:20,paddingVertical:8,overflow:'hidden' }, - disabledoption:{ paddingHorizontal:20,paddingVertical:8,flexDirection:'row',alignItems:'center', backgroundColor:'whitesmoke',opacity:0.9} - -}) + wrapper: { + borderWidth: 1, + borderRadius: 10, + borderColor: 'gray', + paddingHorizontal: 20, + paddingVertical: 12, + flexDirection: 'row', + justifyContent: 'space-between' + }, + dropdown: { + borderWidth: 1, + borderRadius: 10, + borderColor: 'gray', + marginTop: 10, + overflow: 'hidden' + }, + option: { + paddingHorizontal: 20, + paddingVertical: 8, + overflow: 'hidden' + }, + disabledoption: { + paddingHorizontal: 20, + paddingVertical: 8, + flexDirection: 'row', + alignItems: 'center', + backgroundColor: 'whitesmoke', + opacity: 0.9 + } +});