Skip to content

Commit 15eddca

Browse files
committed
feat(hooks): new hook and base major
1 parent e8182fc commit 15eddca

File tree

8 files changed

+15942
-19
lines changed

8 files changed

+15942
-19
lines changed

example/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
"web": "expo start --web"
1010
},
1111
"dependencies": {
12+
"@expo/metro-runtime": "~3.2.1",
1213
"expo": "~51.0.19",
1314
"expo-status-bar": "~1.12.1",
1415
"react": "18.2.0",
15-
"react-native": "0.74.3",
16-
"@expo/metro-runtime": "~3.2.1",
1716
"react-dom": "18.2.0",
17+
"react-native": "0.74.3",
1818
"react-native-web": "~0.19.10"
1919
},
2020
"devDependencies": {

example/src/App.tsx

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,37 @@
1-
import { useState, useEffect } from 'react';
2-
import { StyleSheet, View, Text } from 'react-native';
3-
import { multiply } from '@appandflow/react-native-google-autocomplete';
1+
import { StyleSheet, View, Text, TextInput, ScrollView } from 'react-native';
2+
import { useGoogleAutocomplete } from '@appandflow/react-native-google-autocomplete';
43

5-
export default function App() {
6-
const [result, setResult] = useState<number | undefined>();
4+
const API_KEY = '';
75

8-
useEffect(() => {
9-
multiply(3, 7).then(setResult);
10-
}, []);
6+
export default function App() {
7+
const { setTerm, locationResults, isSearching } = useGoogleAutocomplete(
8+
API_KEY,
9+
{
10+
language: 'en',
11+
minLength: 3,
12+
}
13+
);
1114

1215
return (
1316
<View style={styles.container}>
14-
<Text>Result: {result}</Text>
17+
<View style={styles.box}>
18+
<TextInput
19+
placeholder="Search"
20+
onChangeText={setTerm}
21+
style={styles.input}
22+
/>
23+
</View>
24+
25+
<ScrollView contentContainerStyle={{ paddingHorizontal: 16 }}>
26+
{isSearching && locationResults.length === 0 && (
27+
<Text>Searching...</Text>
28+
)}
29+
{locationResults.map((location) => (
30+
<View key={location.id}>
31+
<Text>{location.structured_formatting.main_text}</Text>
32+
</View>
33+
))}
34+
</ScrollView>
1535
</View>
1636
);
1737
}
@@ -22,9 +42,16 @@ const styles = StyleSheet.create({
2242
alignItems: 'center',
2343
justifyContent: 'center',
2444
},
45+
input: {
46+
height: 50,
47+
borderRadius: 10,
48+
borderWidth: 1,
49+
width: '100%',
50+
marginTop: 100,
51+
paddingHorizontal: 16,
52+
},
2553
box: {
26-
width: 60,
27-
height: 60,
28-
marginVertical: 20,
54+
paddingHorizontal: 16,
55+
width: '100%',
2956
},
3057
});

package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@appandflow/react-native-google-autocomplete",
3-
"version": "0.1.0",
3+
"version": "1.0.0",
44
"description": "A library to help you use google places autocomplete",
55
"source": "./src/index.tsx",
66
"main": "./lib/commonjs/index.cjs",
@@ -38,7 +38,8 @@
3838
"lint": "eslint \"**/*.{js,ts,tsx}\"",
3939
"clean": "del-cli lib",
4040
"prepare": "bob build",
41-
"release": "release-it"
41+
"release": "release-it",
42+
"release:beta": "release-it major --preRelease=beta"
4243
},
4344
"keywords": [
4445
"react-native",
@@ -126,6 +127,8 @@
126127
],
127128
"rules": {
128129
"react/react-in-jsx-scope": "off",
130+
"react-hooks/exhaustive-deps": "off",
131+
"react-native/no-inline-styles": "off",
129132
"prettier/prettier": [
130133
"error",
131134
{
@@ -176,5 +179,9 @@
176179
"create-react-native-library": {
177180
"type": "library",
178181
"version": "0.38.1"
182+
},
183+
"dependencies": {
184+
"query-string": "^9.0.0",
185+
"use-debounce": "^10.0.1"
179186
}
180187
}

src/GoogleAutocomplete.tsx

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { useEffect, useState } from 'react';
2+
import {
3+
GoogleService,
4+
type GoogleLocationResult,
5+
} from './services/google.service';
6+
import { useDebounce } from 'use-debounce';
7+
import { useIsMounted } from './useIsMounted';
8+
9+
interface Options {
10+
/**
11+
* Minimun length of the input before start fetching - default: 2
12+
*/
13+
minLength?: number;
14+
15+
/**
16+
* Debounce request time in ms - default: 300
17+
*/
18+
debounce?: number;
19+
20+
/**
21+
* Language for Google query - default: en
22+
*/
23+
language?: string;
24+
25+
/**
26+
* A grouping of places to which you would like to restrict your results
27+
*/
28+
components?: string;
29+
30+
/**
31+
* See https://developers.google.com/places/web-service/autocomplete#place_types = default: address
32+
*/
33+
queryTypes?:
34+
| 'address'
35+
| 'geocode'
36+
| '(cities)'
37+
| 'establishment'
38+
| 'geocode|establishment';
39+
40+
/**
41+
* The distance (in meters) within which to return place results.
42+
* Note that setting a radius biases results to the indicated area,
43+
* but may not fully restrict results to the specified area.
44+
*/
45+
radius?: string;
46+
47+
/**
48+
* The latitude to retrieve place information
49+
*/
50+
lat?: number;
51+
52+
/**
53+
* The longitude to retrieve place information
54+
*/
55+
lng?: number;
56+
57+
/**
58+
* Enable strict mode to return search result only in the area defined by radius, lat and lng
59+
*/
60+
strictBounds?: boolean;
61+
}
62+
63+
export const useGoogleAutocomplete = (apiKey: string, opts: Options = {}) => {
64+
const {
65+
minLength = 2,
66+
debounce = 300,
67+
language = 'en',
68+
queryTypes = 'address',
69+
} = opts;
70+
const isMounted = useIsMounted();
71+
const [isSearching, setIsSearching] = useState(false);
72+
const [term, setTerm] = useState('');
73+
const [debouncedTerm] = useDebounce(term, debounce);
74+
const [locationResults, setLocationResults] = useState<
75+
GoogleLocationResult[]
76+
>([]);
77+
const [searchError, setSearchError] = useState<Error | null>(null);
78+
79+
const search = async (value: string) => {
80+
setIsSearching(true);
81+
try {
82+
const results = await GoogleService.search(value, {
83+
key: apiKey,
84+
language,
85+
types: queryTypes,
86+
strictBounds: opts.strictBounds,
87+
lat: opts.lat,
88+
lng: opts.lng,
89+
radius: opts.radius,
90+
});
91+
92+
setLocationResults(results.predictions);
93+
} catch (error) {
94+
if (error instanceof Error) {
95+
setSearchError(error);
96+
}
97+
} finally {
98+
setIsSearching(false);
99+
}
100+
};
101+
102+
const searchDetails = async (placeId: string) => {
103+
return GoogleService.searchDetails(placeId, {
104+
key: apiKey,
105+
language,
106+
types: queryTypes,
107+
components: opts.components,
108+
});
109+
};
110+
111+
const clearSearch = () => {
112+
if (isMounted()) {
113+
setLocationResults([]);
114+
setIsSearching(false);
115+
}
116+
};
117+
118+
useEffect(() => {
119+
if (debouncedTerm.length >= minLength) {
120+
search(debouncedTerm);
121+
}
122+
}, [debouncedTerm]);
123+
124+
return {
125+
locationResults,
126+
isSearching,
127+
searchError,
128+
clearSearch,
129+
setTerm,
130+
searchDetails,
131+
};
132+
};

src/index.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1-
export function multiply(a: number, b: number): Promise<number> {
2-
return Promise.resolve(a * b);
3-
}
1+
import { useGoogleAutocomplete } from './GoogleAutocomplete';
2+
import {
3+
type GoogleLocationDetailResult,
4+
type GoogleLocationResult,
5+
} from './services/google.service';
6+
7+
export {
8+
useGoogleAutocomplete,
9+
type GoogleLocationDetailResult,
10+
type GoogleLocationResult,
11+
};

0 commit comments

Comments
 (0)