Skip to content

Instantly share code, notes, and snippets.

@adhendo
Created June 29, 2021 16:49
Show Gist options
  • Save adhendo/104a73c431e582fcfdb789bbe4a1cbf6 to your computer and use it in GitHub Desktop.
Save adhendo/104a73c431e582fcfdb789bbe4a1cbf6 to your computer and use it in GitHub Desktop.
React Native Search Screen Example
import { EvilIcons, Feather } from "@expo/vector-icons";
import AppLoading from "expo-app-loading";
import * as Expo from "expo-asset";
import PropTypes from "prop-types";
import React from "react";
import {
Image,
Keyboard,
SafeAreaView,
StatusBar,
StyleSheet,
TextInput,
TouchableOpacity,
TouchableWithoutFeedback,
View,
} from "react-native";
import { ELASTIC_URL } from "react-native-dotenv";
import { debounce, throttle } from "throttle-debounce";
import WH_LOGO from "../../assets/LOGO.png";
// Config
import colours from "../../config/colours";
// Components
import Suggestions from "./Suggestions";
// Cache images
function cacheImages(images) {
return images.map((image) => {
if (typeof image === "string") {
return Image.prefetch(image);
}
return Expo.Asset.fromModule(image).downloadAsync();
});
}
// GA tracking
//const ID = Expo.Constants.manifest.extra.googleAnalytics;
//const analytics = new Analytics(ID);
export default class SearchScreen extends React.Component {
static get propTypes() {
return {
navigation: PropTypes.object.isRequired,
};
}
constructor(props) {
super(props);
this.state = { results: [], text: null, showLogo: true };
this.throttleSearch = throttle(400, this.getInfo);
this.debounceSearch = debounce(700, this.getInfo);
this.cache = {}; // caching autocomplete results
}
componentDidMount() {
//Expo.Amplitude.initialize("6460727d017e832e2083e13916c7c9e5");
//Expo.Amplitude.logEvent("SCREEN: Search");
//analytics.hit(new ScreenHit("SCREEN: Search"));
}
componentDidUpdate(prevProps, prevState) {
const { text } = this.state;
if (text !== prevState.text) {
if (text.length >= 1) {
if (text.length < 5 || text.endsWith(" ")) this.throttleSearch(text);
else this.debounceSearch(text);
} else {
this.submitAndClear();
}
}
}
// Load logos
_loadAssetsAsync = async () => {
const imageAssets = cacheImages([WH_LOGO]);
await Promise.all([...imageAssets]);
};
getInfo = () => {
const { text } = this.state;
const url = `${ELASTIC_URL}`;
const cached = this.cache[url];
if (cached) {
this.setState({ results: cached, showLogo: false });
return;
}
fetch(url)
.then((response) => response.json())
.then((data) => {
this.cache[url] = data.hits.hits;
this.setState({
results: data.hits.hits,
showLogo: false,
});
});
};
submitAndClear = () => {
this.setState({ text: "", showLogo: true });
Keyboard.dismiss();
};
render() {
const { isReady, showLogo, text, results } = this.state;
const { navigation } = this.props;
if (!isReady) {
return (
<AppLoading
startAsync={this._loadAssetsAsync}
onFinish={() => this.setState({ isReady: true })}
onError={console.warn}
/>
);
}
return (
<SafeAreaView style={styles.safeView}>
<StatusBar barStyle="dark-content" />
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<View style={styles.container}>
<View style={styles.topRow}>
<TouchableOpacity
onPress={() => {
navigation.goBack();
}}
style={styles.backBtn}>
<EvilIcons name="chevron-left" size={30} color="#000" />
</TouchableOpacity>
<View style={styles.searchContainer}>
<TextInput
style={styles.TextInput}
onChangeText={(changedText) =>
this.setState({ text: changedText })
}
autoFocus={true}
autoCapitalize={true}
value={text}
placeholder="Search..."
placeholderTextColor="grey"
clearButtonMode="always"
/>
</View>
</View>
{showLogo && <Image style={styles.logo} source={WH_LOGO} />}
{results.length > 0 && text.length > 0 && (
<Suggestions
style={styles.Suggestions}
results={results}
navigation={navigation}
/>
)}
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
);
}
}
// Styles
const styles = StyleSheet.create({
safeView: {
flex: 1,
},
container: {
flex: 1,
alignItems: "center",
paddingBottom: 30,
},
topRow: {
flexDirection: "row",
width: "100%",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 20,
marginBottom: 15,
marginTop: 10
},
tagsRow: {
flexDirection: "row",
width: "100%",
marginTop: 10,
marginBottom: 8
},
logo: {
width: 80,
height: 200,
marginBottom: 60,
marginTop: "50%",
resizeMode: "cover",
alignSelf: "center",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.20,
shadowRadius: 1.41,
elevation: 2,
},
searchContainer: {
flexDirection: "row",
flexWrap: "nowrap",
width: "87.5%",
backgroundColor: "rgb(253,253,252)",
borderRadius: 12,
paddingTop: 14,
paddingBottom: 14,
paddingLeft: 4,
alignItems: "center",
justifyContent: "center",
borderBottomColor: "#fff",
borderBottomWidth: 0.25,
zIndex: 9999999999
},
TextInput: {
flex: 1,
fontSize: 16,
marginLeft: 14,
marginRight: 14,
textAlign: "left",
alignItems: "center",
flexWrap: "nowrap",
color: "#333",
},
Suggestions: {
flex: 1,
alignItems: "center",
color: colours.primaryWhite,
},
creditsContainer: {
flexDirection: "row",
width: 170,
},
creditsText: {
fontSize: 12,
color: colours.secondaryGrey,
textAlign: "left",
paddingLeft: 20,
},
creditsImage: {
width: 30,
height: 30,
opacity: 0.2,
alignSelf: "flex-start",
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment