Advertisement
duongntb94

[Feature] [RN] Search with pagination sample

Oct 28th, 2019
368
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* eslint-disable no-nested-ternary */
  2. import React from 'react';
  3. import {
  4.   FlatList, RefreshControl, StyleSheet, TextInput, View, TouchableOpacity, ActivityIndicator
  5. } from 'react-native';
  6. import {
  7.   Text, TouchableText, Seperator, Touhca
  8. } from 'src/components';
  9. import { ColorScheme } from 'src/constants';
  10. import i18next from 'src/locale';
  11. import { HomeStore, APIStore } from 'src/stores';
  12. import _ from 'lodash';
  13.  
  14. const PAGE_SIZE = 20;
  15.  
  16. type Props = {
  17.   onPressSelectCompany: Function,
  18. };
  19.  
  20. type CompanyProps = {
  21.   id: String,
  22.   email: String,
  23.   name: String,
  24.   organisationNumber: String,
  25.   isSpecial: Number,
  26.   status: Number,
  27.   subscriptionId: Number,
  28. }
  29.  
  30. type State = {
  31.   companies: CompanyProps[],
  32.   refreshing: Boolean,
  33.   keyword: String,
  34.   selectedCompany: Object,
  35.   page: Number,
  36.   fetching: Boolean, // Indication that the component is fetching/downloading data from api.
  37. };
  38.  
  39. /**
  40.  * Format company.
  41.  * @param {*} company input company.
  42.  */
  43. const formatCompany = (company: Object): CompanyProps => ({
  44.   id: company.CompanyId,
  45.   name: company.CompanyName || '',
  46.   organisationNumber: company.OrganisationNumber,
  47.   isSpecial: company.IsSpecial,
  48.   email: company.Email || '',
  49.   subscriptionId: company.SubscriptionId,
  50.   status: company.Status,
  51. });
  52.  
  53. export default class CompanyAssignedView extends React.Component<Props, State> {
  54.   // Indication that the component is working.
  55.   mounted = false;
  56.  
  57.   state = {
  58.     companies: HomeStore.companies.slice(),
  59.     refreshing: false,
  60.     keyword: '',
  61.     selectedCompany: HomeStore.selectedCompany || null,
  62.     page: 1, // Variable for pagination data.
  63.     fetching: false,
  64.   };
  65.  
  66.   fetchCompanies = _.debounce(async (reset = false) => {
  67.     const { keyword, page } = this.state;
  68.     const searchPage = reset ? 1 : page;
  69.     const { data: rawCompanies } = await APIStore.fetchCompany(searchPage, PAGE_SIZE, keyword);
  70.     const formattedCompanies = rawCompanies.map(item => formatCompany(item));
  71.     this.mounted && this.setState(prevState => ({
  72.       companies: reset ? formattedCompanies : [...prevState.companies, ...formattedCompanies],
  73.       fetching: false,
  74.       page: reset ? 2 : prevState.page + 1,
  75.     }));
  76.   }, 300);
  77.  
  78.   componentDidMount() {
  79.     this.mounted = true;
  80.     this.setState({ fetching: true }, () => this.fetchCompanies(true));
  81.   }
  82.  
  83.   componentWillUnmount() {
  84.     this.mounted = false;
  85.   }
  86.  
  87.   renderRow = ({ item }) => {
  88.     const hId = _.get(item, ['id'], '');
  89.     const cId = _.get(this.state, ['selectedCompany', 'id'], '');
  90.     const isSelected = hId === cId;
  91.     return (
  92.       <TouchableOpacity
  93.         style={styles.companyRow}
  94.         onPress={() => this.setState({ selectedCompany: item })}
  95.       >
  96.         <View style={styles.flex1}>
  97.           <Text
  98.             style={isSelected ? styles.selectedCompanyText : null}
  99.             size15
  100.             medium
  101.           >
  102.             {item.name}
  103.           </Text>
  104.           {!!item.email && (
  105.             <Text size13 gray>
  106.               {item.email}
  107.             </Text>
  108.           )}
  109.           {!!item.organisationNumber && (
  110.             <Text size12 gray>
  111.               {item.organisationNumber}
  112.             </Text>
  113.           )}
  114.         </View>
  115.         <View style={styles.selectedCompanyView}>
  116.           {isSelected && <View style={styles.dot} />}
  117.         </View>
  118.       </TouchableOpacity>
  119.     );
  120.   };
  121.  
  122.   keyExtractor = (item, index) => `${index}`;
  123.  
  124.   renderSeperator = () => (<Seperator />)
  125.  
  126.   onRefresh = async () => {
  127.     this.mounted && this.setState({ refreshing: true, page: 1 });
  128.     await this.fetchCompanies(true);
  129.     this.mounted && this.setState({ refreshing: false });
  130.   };
  131.  
  132.   onPressSelect = () => {
  133.     const { onPressSelectCompany } = this.props;
  134.     const { selectedCompany } = this.state;
  135.     onPressSelectCompany && onPressSelectCompany(selectedCompany);
  136.   }
  137.  
  138.   onChangeText = (text) => {
  139.     const searchText = text.trim().toLowerCase();
  140.     this.fetchCompanies.cancel();
  141.     this.setState({
  142.       keyword: searchText,
  143.       fetching: true,
  144.       page: 1,
  145.     }, () => this.fetchCompanies(true));
  146.   }
  147.  
  148.   reachedListThreshold = () => {
  149.     // If the current list have less than 5 items.
  150.     // That means we don't have more item because the PAGE_SIZE is 20.
  151.     // Stop fetching to make the api do not call page 2+
  152.     const { companies } = this.state;
  153.     if (companies.length <= 5) {
  154.       return;
  155.     }
  156.     // Start searching.
  157.     this.fetchCompanies.cancel();
  158.     this.setState({
  159.       fetching: true,
  160.     }, () => this.fetchCompanies());
  161.   }
  162.  
  163.   render() {
  164.     const {
  165.       refreshing, selectedCompany, companies, fetching,
  166.     } = this.state;
  167.     return (
  168.       <View style={styles.container}>
  169.         {/* Search view */}
  170.         <View style={styles.searchView}>
  171.           <TextInput
  172.             placeholder={i18next.t('home.searchCompanyName')}
  173.             style={styles.searchInput}
  174.             onChangeText={keyword => this.onChangeText(keyword)}
  175.           />
  176.           { fetching && (<ActivityIndicator style={styles.indicator} />)}
  177.         </View>
  178.         {/* Body Flatlist */}
  179.         <FlatList
  180.           style={styles.list}
  181.           data={companies}
  182.           extraData={`${companies.length}+${selectedCompany ? selectedCompany.id : ''}`}
  183.           renderItem={this.renderRow}
  184.           keyExtractor={this.keyExtractor}
  185.           refreshControl={(<RefreshControl refreshing={refreshing} onRefresh={this.onRefresh} />)}
  186.           ItemSeparatorComponent={this.renderSeperator}
  187.           onEndReached={this.reachedListThreshold}
  188.           onEndReachedThreshold={0.1}
  189.         />
  190.         <TouchableText
  191.           style={styles.selectButton}
  192.           textStyle={styles.selectText}
  193.           text={i18next.t('home.select')}
  194.           onPress={this.onPressSelect}
  195.         />
  196.       </View>
  197.     );
  198.   }
  199. }
  200.  
  201. const styles = StyleSheet.create({
  202.   container: {
  203.   },
  204.   list: {
  205.     height: 300,
  206.   },
  207.   searchView: {
  208.     flexDirection: 'row',
  209.     height: 44,
  210.     alignItems: 'center',
  211.     paddingHorizontal: 15,
  212.     marginHorizontal: 20,
  213.     borderColor: ColorScheme.borderGray,
  214.     borderWidth: 1,
  215.     borderRadius: 10,
  216.     marginTop: 10,
  217.   },
  218.   searchInput: {
  219.     flex: 1,
  220.     alignSelf: 'stretch',
  221.   },
  222.   purchaseButton: {
  223.     borderRadius: 5,
  224.     borderColor: ColorScheme.primary,
  225.     borderWidth: 1,
  226.     height: 35,
  227.     paddingHorizontal: 5,
  228.     marginLeft: 5,
  229.   },
  230.   purchaseText: {
  231.     color: ColorScheme.primary,
  232.   },
  233.   row: {
  234.     flexDirection: 'row',
  235.     alignSelf: 'stretch',
  236.     alignItems: 'center',
  237.     marginHorizontal: 20,
  238.     marginVertical: 10,
  239.   },
  240.   flex1: {
  241.     flex: 1,
  242.   },
  243.   verifiedText: {
  244.     color: ColorScheme.blue,
  245.   },
  246.   companyRow: {
  247.     alignSelf: 'stretch',
  248.     marginHorizontal: 20,
  249.     marginVertical: 10,
  250.     flexDirection: 'row',
  251.     alignItems: 'center',
  252.   },
  253.   selectButton: {
  254.     height: 52,
  255.     borderRadius: 10,
  256.     backgroundColor: '#2196f3',
  257.     marginHorizontal: 16,
  258.     marginTop: 1,
  259.     marginBottom: 10,
  260.   },
  261.   selectText: {
  262.     fontSize: 16,
  263.     color: ColorScheme.white,
  264.   },
  265.   dot: {
  266.     width: 12,
  267.     height: 12,
  268.     borderRadius: 6,
  269.     backgroundColor: ColorScheme.orange,
  270.   },
  271.   indicator: {
  272.     marginLeft: 10,
  273.   },
  274. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement