Skip to content

Commit 64e331a

Browse files
committed
docs(example): added react-native autosuggest
1 parent 84024e0 commit 64e331a

17 files changed

+13561
-9
lines changed

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
coverage/
33
dist/
44
node_modules/
5+
examples/

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,5 @@ $RECYCLE.BIN/
212212
.dccache
213213

214214
pacts/
215+
216+
.idea

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ A JavaScript library to make requests to the [what3words REST API][api]. __Now w
1919
* [Examples](#examples)
2020
* [CustomTransport](#custom-transport)
2121
* [Autosuggest](#autosuggest)
22+
* [React Native Autosuggest](examples/react-native/autosuggest/readme.md)
2223
* [Convert to Coordinates](#convert-to-coordinates)
2324
* [Convert to Three Word Address](#convert-to-three-word-address)
2425
* [Available Languages](#available-languages)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
node_modules/
2+
.expo/
3+
dist/
4+
npm-debug.*
5+
*.jks
6+
*.p8
7+
*.p12
8+
*.key
9+
*.mobileprovision
10+
*.orig.*
11+
web-build/
12+
13+
# macOS
14+
.DS_Store
15+
16+
# Temporary files created by Metro to check the health of the file watcher
17+
.metro-health-check*
18+
19+
.idea
+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import { StatusBar } from 'expo-status-bar';
2+
import {
3+
FlatList,
4+
Pressable,
5+
StyleSheet,
6+
Text,
7+
TextInput,
8+
View,
9+
} from 'react-native';
10+
import { AutosuggestClient, AutosuggestSuggestion } from '@what3words/api';
11+
import { useState } from 'react';
12+
13+
const API_KEY = '<YOUR_API_KEY>';
14+
const DEBOUNCE_TIME = 300;
15+
16+
const client: AutosuggestClient = AutosuggestClient.init(API_KEY);
17+
18+
const white = '#ffffff';
19+
const blue = '#0a3049';
20+
const red = '#e11f26';
21+
const lightGrey = '#e0e0e0';
22+
const lightBlue = '#dbeffa';
23+
24+
let timeoutId: NodeJS.Timeout | null = null;
25+
26+
export default function App() {
27+
const [search, setSearch] = useState<string>('');
28+
const [suggestions, setSuggestions] = useState<AutosuggestSuggestion[]>([]);
29+
const [isLoading, setIsLoading] = useState<boolean>(false);
30+
const [error, setError] = useState<Error | null>(null);
31+
32+
/**
33+
* Fetch suggestions from the API when the search input value changes
34+
* @param {string} search The search input value
35+
* @returns {Promise<void>}
36+
*/
37+
async function handleOnSearch(search: string): Promise<void> {
38+
setSearch(search);
39+
40+
// Don't search if the input is empty or too short
41+
if (!search || search.length < 3) return;
42+
43+
// Cancel previous request before sending a new one
44+
if (timeoutId) clearTimeout(timeoutId);
45+
46+
timeoutId = setTimeout(async () => {
47+
try {
48+
setIsLoading(true);
49+
setError(null);
50+
const options = { input: search };
51+
const { suggestions } = await client.run(options);
52+
setSuggestions(suggestions);
53+
} catch (error) {
54+
setError(error);
55+
} finally {
56+
setIsLoading(false);
57+
}
58+
}, DEBOUNCE_TIME);
59+
}
60+
61+
/**
62+
* Set the selected suggestion words as the search input value and clear the suggestions list
63+
* @param {AutosuggestSuggestion} sug The selected suggestion
64+
*/
65+
function handleSuggestionPress(sug: { item: AutosuggestSuggestion }): void {
66+
setSearch(sug.item.words);
67+
setSuggestions([]);
68+
}
69+
70+
/**
71+
* Render a suggestion as a Pressable RN component
72+
* @param {AutosuggestSuggestion} sug The suggestion to render
73+
* @returns {JSX.Element}
74+
*/
75+
const renderItem = (sug: { item: AutosuggestSuggestion }): JSX.Element => (
76+
<Pressable
77+
onPress={() => handleSuggestionPress(sug)}
78+
style={({ pressed }) => [
79+
styles.suggestion,
80+
pressed ? { backgroundColor: lightBlue } : {},
81+
]}>
82+
<Text style={styles.suggestionWords}>
83+
<Text style={styles.suggestionTripleSlash}>///</Text>
84+
{sug.item.words}
85+
</Text>
86+
<Text style={styles.suggestionNearestPlace}>
87+
{sug.item.nearestPlace}, {sug.item.country}
88+
</Text>
89+
</Pressable>
90+
);
91+
92+
const showErrorMessage = !isLoading && !suggestions.length && error;
93+
94+
return (
95+
<View style={styles.container}>
96+
<StatusBar style="auto" />
97+
98+
<View style={styles.inputWrapper}>
99+
<TextInput
100+
style={styles.input}
101+
placeholder="/// filled.count.soap"
102+
onChangeText={handleOnSearch}
103+
value={search}
104+
cursorColor={blue}
105+
autoCapitalize="none"
106+
autoComplete="off"
107+
autoCorrect={false}
108+
autoFocus={true}
109+
/>
110+
{showErrorMessage && (
111+
<Text style={styles.errorMessage}>{error.message}</Text>
112+
)}
113+
</View>
114+
115+
<FlatList
116+
style={styles.suggestions}
117+
data={suggestions}
118+
keyExtractor={sug => sug.words}
119+
renderItem={renderItem}
120+
/>
121+
</View>
122+
);
123+
}
124+
125+
const styles = StyleSheet.create({
126+
container: {
127+
flex: 1,
128+
padding: 16,
129+
paddingVertical: 48,
130+
backgroundColor: white,
131+
alignItems: 'center',
132+
justifyContent: 'flex-start',
133+
},
134+
inputWrapper: {
135+
width: '100%',
136+
},
137+
input: {
138+
height: 48,
139+
width: '100%',
140+
color: blue,
141+
paddingVertical: 8,
142+
paddingHorizontal: 16,
143+
fontSize: 20,
144+
borderWidth: 1,
145+
borderColor: lightGrey,
146+
},
147+
suggestions: {
148+
flex: 1,
149+
width: '100%',
150+
backgroundColor: white,
151+
},
152+
suggestion: {
153+
flex: 1,
154+
width: '100%',
155+
flexDirection: 'column',
156+
paddingVertical: 8,
157+
paddingHorizontal: 16,
158+
backgroundColor: white,
159+
borderWidth: 1,
160+
borderTopColor: 'transparent',
161+
borderColor: lightGrey,
162+
},
163+
suggestionTripleSlash: {
164+
color: red,
165+
},
166+
suggestionWords: {
167+
fontSize: 20,
168+
fontWeight: 'bold',
169+
color: blue,
170+
marginBottom: 4,
171+
},
172+
suggestionNearestPlace: {
173+
fontSize: 16,
174+
color: blue,
175+
},
176+
errorMessage: {
177+
color: red,
178+
paddingVertical: 4,
179+
},
180+
});
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"expo": {
3+
"name": "autosuggest",
4+
"slug": "autosuggest",
5+
"version": "1.0.0",
6+
"orientation": "portrait",
7+
"icon": "./assets/icon.png",
8+
"userInterfaceStyle": "light",
9+
"splash": {
10+
"image": "./assets/splash.png",
11+
"resizeMode": "contain",
12+
"backgroundColor": "#ffffff"
13+
},
14+
"assetBundlePatterns": [
15+
"**/*"
16+
],
17+
"ios": {
18+
"supportsTablet": true
19+
},
20+
"android": {
21+
"adaptiveIcon": {
22+
"foregroundImage": "./assets/adaptive-icon.png",
23+
"backgroundColor": "#ffffff"
24+
}
25+
},
26+
"web": {
27+
"favicon": "./assets/favicon.png"
28+
}
29+
}
30+
}
Loading
Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = function(api) {
2+
api.cache(true);
3+
return {
4+
presets: ['babel-preset-expo'],
5+
};
6+
};

0 commit comments

Comments
 (0)