Skip to content

Commit

Permalink
Merge pull request #139 from onmotion/fix-110
Browse files Browse the repository at this point in the history
Fix excess re-renders #110
  • Loading branch information
onmotion authored Oct 6, 2024
2 parents b20e585 + 8552259 commit 5833d57
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 20 deletions.
5 changes: 3 additions & 2 deletions example/components/LocalDataSetExample.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { memo, useState } from 'react'
import { Text, View } from 'react-native'
import type { AutocompleteDropdownItem } from 'react-native-autocomplete-dropdown'
import type { AutocompleteDropdownItem, IAutocompleteDropdownProps } from 'react-native-autocomplete-dropdown'
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown'

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

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

return (
Expand All @@ -23,6 +23,7 @@ export const LocalDataSetExample = memo(() => {
]}
ItemSeparatorComponent={ItemSeparatorComponent}
ignoreAccents
{...props}
/>
<Text style={{ color: '#668', fontSize: 13 }}>Selected item: {JSON.stringify(selectedItem)}</Text>
</>
Expand Down
5 changes: 3 additions & 2 deletions example/components/LocalDataSetExample2.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { memo, useState } from 'react'
import { Dimensions, Text } from 'react-native'
import type { AutocompleteDropdownItem } from 'react-native-autocomplete-dropdown'
import type { AutocompleteDropdownItem, IAutocompleteDropdownProps } from 'react-native-autocomplete-dropdown'
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown'

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

return (
Expand Down Expand Up @@ -32,6 +32,7 @@ export const LocalDataSetExample2 = memo(() => {
-= {item.title} =-
</Text>
)}
{...props}
/>
<Text style={{ color: '#668', fontSize: 13 }}>Selected item: {JSON.stringify(selectedItem)}</Text>
</>
Expand Down
5 changes: 3 additions & 2 deletions example/components/RemoteDataSetExample.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { memo, useCallback, useState } from 'react'
import { Text } from 'react-native'
import type { AutocompleteDropdownItem } from 'react-native-autocomplete-dropdown'
import type { AutocompleteDropdownItem, IAutocompleteDropdownProps } from 'react-native-autocomplete-dropdown'
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown'

export const RemoteDataSetExample = memo(() => {
export const RemoteDataSetExample = memo((props: Omit<IAutocompleteDropdownProps, 'ref' | 'dataSet'>) => {
const [loading, setLoading] = useState(false)
const [remoteDataSet, setRemoteDataSet] = useState<AutocompleteDropdownItem[] | null>(null)
const [selectedItem, setSelectedItem] = useState<AutocompleteDropdownItem | null>(null)
Expand Down Expand Up @@ -52,6 +52,7 @@ export const RemoteDataSetExample = memo(() => {
color: '#8f3c96',
}}
EmptyResultComponent={<Text style={{ padding: 10, fontSize: 15 }}>Oops ¯\_(ツ)_/¯</Text>}
{...props}
/>
<Text style={{ color: '#668', fontSize: 13 }}>Selected item: {JSON.stringify(selectedItem)}</Text>
</>
Expand Down
9 changes: 7 additions & 2 deletions example/components/RemoteDataSetExample2.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import React, { memo, useCallback, useRef, useState } from 'react'
import { Button, Dimensions, Text, View } from 'react-native'
import type { IAutocompleteDropdownRef, AutocompleteDropdownItem } from 'react-native-autocomplete-dropdown'
import type {
IAutocompleteDropdownRef,
AutocompleteDropdownItem,
IAutocompleteDropdownProps,
} from 'react-native-autocomplete-dropdown'
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown'

export const RemoteDataSetExample2 = memo(() => {
export const RemoteDataSetExample2 = memo((props: Omit<IAutocompleteDropdownProps, 'ref' | 'dataSet'>) => {
const [loading, setLoading] = useState(false)
const [suggestionsList, setSuggestionsList] = useState<AutocompleteDropdownItem[] | null>(null)
const [selectedItem, setSelectedItem] = useState<string | null>(null)
Expand Down Expand Up @@ -93,6 +97,7 @@ export const RemoteDataSetExample2 = memo(() => {
showChevron={false}
closeOnBlur={false}
// showClear={false}
{...props}
/>
<View style={{ width: 10 }} />
<Button title="Toggle" onPress={() => dropdownController.current?.toggle()} />
Expand Down
23 changes: 17 additions & 6 deletions src/AutocompleteDropdownContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export interface IAutocompleteDropdownContext {
direction?: 'up' | 'down'
setDirection: Dispatch<SetStateAction<IAutocompleteDropdownContext['direction']>>
activeInputContainerRef?: MutableRefObject<View | null>
controllerRef?: MutableRefObject<IAutocompleteDropdownRef | null>
activeControllerRef?: MutableRefObject<IAutocompleteDropdownRef | null>
controllerRefs?: MutableRefObject<IAutocompleteDropdownRef[]>
}

export interface IAutocompleteDropdownContextProviderProps {
Expand All @@ -24,7 +25,8 @@ export const AutocompleteDropdownContext = React.createContext<IAutocompleteDrop
direction: undefined,
setDirection: () => null,
activeInputContainerRef: undefined,
controllerRef: undefined,
activeControllerRef: undefined,
controllerRefs: undefined,
})

export const AutocompleteDropdownContextProvider: FC<IAutocompleteDropdownContextProviderProps> = ({
Expand All @@ -43,7 +45,8 @@ export const AutocompleteDropdownContextProvider: FC<IAutocompleteDropdownContex
undefined,
)
const activeInputContainerRef = useRef<View>(null)
const controllerRef = useRef<IAutocompleteDropdownRef | null>(null)
const activeControllerRef = useRef<IAutocompleteDropdownRef | null>(null)
const controllerRefs = useRef<IAutocompleteDropdownRef[]>([])
const positionTrackingIntervalRef = useRef<NodeJS.Timeout>()

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

return (
<AutocompleteDropdownContext.Provider
value={{ content, setContent, activeInputContainerRef, direction, setDirection, controllerRef }}>
value={{
content,
setContent,
activeInputContainerRef,
direction,
setDirection,
activeControllerRef,
controllerRefs,
}}>
<View
style={styles.clickOutsideHandlerArea}
onTouchEnd={() => {
controllerRef.current?.close()
controllerRef.current?.blur()
activeControllerRef.current?.close()
activeControllerRef.current?.blur()
}}>
{children}
</View>
Expand Down
28 changes: 22 additions & 6 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,10 @@ export const AutocompleteDropdown = memo<
content,
setContent,
activeInputContainerRef,
controllerRef,
activeControllerRef,
direction = directionProp,
setDirection,
controllerRefs,
} = useContext(AutocompleteDropdownContext)
const themeName = useColorScheme() || 'light'
const styles = useMemo(() => getStyles(themeName), [themeName])
Expand Down Expand Up @@ -220,18 +221,32 @@ export const AutocompleteDropdown = memo<
setSelectedItem(item)
}, [])

useEffect(() => {
if (activeControllerRef?.current) {
controllerRefs?.current.push(activeControllerRef?.current)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

const closeAll = useCallback(() => {
controllerRefs?.current.forEach(c => {
c?.blur?.()
c?.close?.()
})
}, [controllerRefs])

/** expose controller methods */
useEffect(() => {
const methods = controllerRef ? { close, blur, open, toggle, clear, setInputText, setItem } : null
if (controllerRef) {
controllerRef.current = methods
const methods = activeControllerRef ? { close, blur, open, toggle, clear, setInputText, setItem } : null
if (activeControllerRef) {
activeControllerRef.current = methods
}
if (typeof controller === 'function') {
controller(methods)
} else if (controller) {
controller.current = methods
}
}, [blur, clear, close, controller, controllerRef, open, setInputText, setItem, toggle])
}, [blur, clear, close, controller, activeControllerRef, open, setInputText, setItem, toggle])

useEffect(() => {
if (selectedItem) {
Expand Down Expand Up @@ -407,13 +422,14 @@ export const AutocompleteDropdown = memo<

const onPressOut = useCallback(
(e: GestureResponderEvent) => {
closeAll()
if (editable) {
inputRef?.current?.focus()
} else {
toggle()
}
},
[editable, toggle],
[closeAll, editable, toggle],
)

useEffect(() => {
Expand Down

0 comments on commit 5833d57

Please sign in to comment.