Skip to content

Commit 5833d57

Browse files
authored
Merge pull request #139 from onmotion/fix-110
Fix excess re-renders #110
2 parents b20e585 + 8552259 commit 5833d57

File tree

6 files changed

+55
-20
lines changed

6 files changed

+55
-20
lines changed

example/components/LocalDataSetExample.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React, { memo, useState } from 'react'
22
import { Text, View } from 'react-native'
3-
import type { AutocompleteDropdownItem } from 'react-native-autocomplete-dropdown'
3+
import type { AutocompleteDropdownItem, IAutocompleteDropdownProps } from 'react-native-autocomplete-dropdown'
44
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown'
55

66
const ItemSeparatorComponent = () => <View style={{ height: 1, width: '100%', backgroundColor: '#d8e1e6' }} />
77

8-
export const LocalDataSetExample = memo(() => {
8+
export const LocalDataSetExample = memo((props: Omit<IAutocompleteDropdownProps, 'ref' | 'dataSet'>) => {
99
const [selectedItem, setSelectedItem] = useState<AutocompleteDropdownItem | null>(null)
1010

1111
return (
@@ -23,6 +23,7 @@ export const LocalDataSetExample = memo(() => {
2323
]}
2424
ItemSeparatorComponent={ItemSeparatorComponent}
2525
ignoreAccents
26+
{...props}
2627
/>
2728
<Text style={{ color: '#668', fontSize: 13 }}>Selected item: {JSON.stringify(selectedItem)}</Text>
2829
</>

example/components/LocalDataSetExample2.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, { memo, useState } from 'react'
22
import { Dimensions, Text } from 'react-native'
3-
import type { AutocompleteDropdownItem } from 'react-native-autocomplete-dropdown'
3+
import type { AutocompleteDropdownItem, IAutocompleteDropdownProps } from 'react-native-autocomplete-dropdown'
44
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown'
55

6-
export const LocalDataSetExample2 = memo(() => {
6+
export const LocalDataSetExample2 = memo((props: Omit<IAutocompleteDropdownProps, 'ref' | 'dataSet'>) => {
77
const [selectedItem, setSelectedItem] = useState<AutocompleteDropdownItem | null>(null)
88

99
return (
@@ -32,6 +32,7 @@ export const LocalDataSetExample2 = memo(() => {
3232
-= {item.title} =-
3333
</Text>
3434
)}
35+
{...props}
3536
/>
3637
<Text style={{ color: '#668', fontSize: 13 }}>Selected item: {JSON.stringify(selectedItem)}</Text>
3738
</>

example/components/RemoteDataSetExample.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, { memo, useCallback, useState } from 'react'
22
import { Text } from 'react-native'
3-
import type { AutocompleteDropdownItem } from 'react-native-autocomplete-dropdown'
3+
import type { AutocompleteDropdownItem, IAutocompleteDropdownProps } from 'react-native-autocomplete-dropdown'
44
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown'
55

6-
export const RemoteDataSetExample = memo(() => {
6+
export const RemoteDataSetExample = memo((props: Omit<IAutocompleteDropdownProps, 'ref' | 'dataSet'>) => {
77
const [loading, setLoading] = useState(false)
88
const [remoteDataSet, setRemoteDataSet] = useState<AutocompleteDropdownItem[] | null>(null)
99
const [selectedItem, setSelectedItem] = useState<AutocompleteDropdownItem | null>(null)
@@ -52,6 +52,7 @@ export const RemoteDataSetExample = memo(() => {
5252
color: '#8f3c96',
5353
}}
5454
EmptyResultComponent={<Text style={{ padding: 10, fontSize: 15 }}>Oops ¯\_(ツ)_/¯</Text>}
55+
{...props}
5556
/>
5657
<Text style={{ color: '#668', fontSize: 13 }}>Selected item: {JSON.stringify(selectedItem)}</Text>
5758
</>

example/components/RemoteDataSetExample2.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import React, { memo, useCallback, useRef, useState } from 'react'
22
import { Button, Dimensions, Text, View } from 'react-native'
3-
import type { IAutocompleteDropdownRef, AutocompleteDropdownItem } from 'react-native-autocomplete-dropdown'
3+
import type {
4+
IAutocompleteDropdownRef,
5+
AutocompleteDropdownItem,
6+
IAutocompleteDropdownProps,
7+
} from 'react-native-autocomplete-dropdown'
48
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown'
59

6-
export const RemoteDataSetExample2 = memo(() => {
10+
export const RemoteDataSetExample2 = memo((props: Omit<IAutocompleteDropdownProps, 'ref' | 'dataSet'>) => {
711
const [loading, setLoading] = useState(false)
812
const [suggestionsList, setSuggestionsList] = useState<AutocompleteDropdownItem[] | null>(null)
913
const [selectedItem, setSelectedItem] = useState<string | null>(null)
@@ -93,6 +97,7 @@ export const RemoteDataSetExample2 = memo(() => {
9397
showChevron={false}
9498
closeOnBlur={false}
9599
// showClear={false}
100+
{...props}
96101
/>
97102
<View style={{ width: 10 }} />
98103
<Button title="Toggle" onPress={() => dropdownController.current?.toggle()} />

src/AutocompleteDropdownContext.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ export interface IAutocompleteDropdownContext {
1010
direction?: 'up' | 'down'
1111
setDirection: Dispatch<SetStateAction<IAutocompleteDropdownContext['direction']>>
1212
activeInputContainerRef?: MutableRefObject<View | null>
13-
controllerRef?: MutableRefObject<IAutocompleteDropdownRef | null>
13+
activeControllerRef?: MutableRefObject<IAutocompleteDropdownRef | null>
14+
controllerRefs?: MutableRefObject<IAutocompleteDropdownRef[]>
1415
}
1516

1617
export interface IAutocompleteDropdownContextProviderProps {
@@ -24,7 +25,8 @@ export const AutocompleteDropdownContext = React.createContext<IAutocompleteDrop
2425
direction: undefined,
2526
setDirection: () => null,
2627
activeInputContainerRef: undefined,
27-
controllerRef: undefined,
28+
activeControllerRef: undefined,
29+
controllerRefs: undefined,
2830
})
2931

3032
export const AutocompleteDropdownContextProvider: FC<IAutocompleteDropdownContextProviderProps> = ({
@@ -43,7 +45,8 @@ export const AutocompleteDropdownContextProvider: FC<IAutocompleteDropdownContex
4345
undefined,
4446
)
4547
const activeInputContainerRef = useRef<View>(null)
46-
const controllerRef = useRef<IAutocompleteDropdownRef | null>(null)
48+
const activeControllerRef = useRef<IAutocompleteDropdownRef | null>(null)
49+
const controllerRefs = useRef<IAutocompleteDropdownRef[]>([])
4750
const positionTrackingIntervalRef = useRef<NodeJS.Timeout>()
4851

4952
useEffect(() => {
@@ -119,12 +122,20 @@ export const AutocompleteDropdownContextProvider: FC<IAutocompleteDropdownContex
119122

120123
return (
121124
<AutocompleteDropdownContext.Provider
122-
value={{ content, setContent, activeInputContainerRef, direction, setDirection, controllerRef }}>
125+
value={{
126+
content,
127+
setContent,
128+
activeInputContainerRef,
129+
direction,
130+
setDirection,
131+
activeControllerRef,
132+
controllerRefs,
133+
}}>
123134
<View
124135
style={styles.clickOutsideHandlerArea}
125136
onTouchEnd={() => {
126-
controllerRef.current?.close()
127-
controllerRef.current?.blur()
137+
activeControllerRef.current?.close()
138+
activeControllerRef.current?.blur()
128139
}}>
129140
{children}
130141
</View>

src/index.tsx

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,10 @@ export const AutocompleteDropdown = memo<
104104
content,
105105
setContent,
106106
activeInputContainerRef,
107-
controllerRef,
107+
activeControllerRef,
108108
direction = directionProp,
109109
setDirection,
110+
controllerRefs,
110111
} = useContext(AutocompleteDropdownContext)
111112
const themeName = useColorScheme() || 'light'
112113
const styles = useMemo(() => getStyles(themeName), [themeName])
@@ -220,18 +221,32 @@ export const AutocompleteDropdown = memo<
220221
setSelectedItem(item)
221222
}, [])
222223

224+
useEffect(() => {
225+
if (activeControllerRef?.current) {
226+
controllerRefs?.current.push(activeControllerRef?.current)
227+
}
228+
// eslint-disable-next-line react-hooks/exhaustive-deps
229+
}, [])
230+
231+
const closeAll = useCallback(() => {
232+
controllerRefs?.current.forEach(c => {
233+
c?.blur?.()
234+
c?.close?.()
235+
})
236+
}, [controllerRefs])
237+
223238
/** expose controller methods */
224239
useEffect(() => {
225-
const methods = controllerRef ? { close, blur, open, toggle, clear, setInputText, setItem } : null
226-
if (controllerRef) {
227-
controllerRef.current = methods
240+
const methods = activeControllerRef ? { close, blur, open, toggle, clear, setInputText, setItem } : null
241+
if (activeControllerRef) {
242+
activeControllerRef.current = methods
228243
}
229244
if (typeof controller === 'function') {
230245
controller(methods)
231246
} else if (controller) {
232247
controller.current = methods
233248
}
234-
}, [blur, clear, close, controller, controllerRef, open, setInputText, setItem, toggle])
249+
}, [blur, clear, close, controller, activeControllerRef, open, setInputText, setItem, toggle])
235250

236251
useEffect(() => {
237252
if (selectedItem) {
@@ -407,13 +422,14 @@ export const AutocompleteDropdown = memo<
407422

408423
const onPressOut = useCallback(
409424
(e: GestureResponderEvent) => {
425+
closeAll()
410426
if (editable) {
411427
inputRef?.current?.focus()
412428
} else {
413429
toggle()
414430
}
415431
},
416-
[editable, toggle],
432+
[closeAll, editable, toggle],
417433
)
418434

419435
useEffect(() => {

0 commit comments

Comments
 (0)