乐趣区

关于javascript:React-Native城市选择与切换

尽管 React Native 曾经不在是以后热门的跨平台技术了,然而还是能够看到很多的公司和集体在用,Flutter 有 Flutter 好,RN 呢也有适宜本人的中央,至于到底哪个更好,咱们不做过多的比拟。
最近,我在降级之前的《React Native 挪动开发实战》一书,书中的我的项目有城市切换的性能,成果如下图所示。

能够看到,这个城市抉择页面是很惯例的,蕴含了以后定位城市和城市列表,右侧能够通过 SlideBar 进行快捷定位,除此之外,此组件还反对搜寻性能。

首先,咱们看一下城市列表,对于这一性能,咱们能够应用 SectionList 组件,因为咱们能够应用 SectionList 的 ListHeaderComponent 属性来实现以后定位布局,而左边的字母索引成果须要借助 SectionList 的 scrollToLocation() 函数,如下所示。

const _scrollTo = (index, letter) => {listViewRef?.current?.scrollToLocation({itemIndex: 0, sectionIndex: index});
  };

而搜寻性能就更加简略了,应用 FlatList 组件展现即可,此处也能够应用 list.map 循环来开发列表性能。
CitySelectScreen.js

import React, {useState, useEffect} from 'react';
import {
  View,
  Text,
  TextInput,
  StyleSheet,
  TouchableOpacity,
  Keyboard,
} from 'react-native';
import PropTypes from 'prop-types';

import {CityList} from './components';
import apiRequest from '../../api';
import Header from '../../common/Header/Header';

const CitySelectScreen = ({location = '上海市', navigation}) => {
  let inputRef = null;
  const [cities, setCities] = useState([]);
  const [currentCityList, setCurrentCityList] = useState({});
  const [isFocused, setIsFocused] = useState(false);
  const [result, setResult] = useState([]);
  const [keyword, setKeyword] = useState('');

  useEffect(() => {getCities();
  }, []);

  const onChangeText = e => {setKeyword(e);
  };

  const onSelectCity = city => {setTimeout(() => {
      navigation.navigate('SelectCinemaScreen', {
        title: city.CITY_NAME,
        CITY_CD: city.CITY_CD,
      });
    }, 200);
    setResult([]);
  };

  const searchSubmit = () => {if (isFocused) {inputRef.blur();
      setIsFocused(false);
      setResult([]);
      Keyboard.dismiss();} else {setIsFocused(true);
      inputRef.focus();}
  };

  const getCities = async () => {
    let url = 'https://prd-api.cgv.com.cn/product/areas/that/group';
    const res = await apiRequest.get(url);
    setCities(res);
  };

  const searchCities = async () => {
    let url = 'https://prd-api.cgv.com.cn/product/areas/that/group';
    const params = {condition: keyword};
    const res = await apiRequest.get(url, params);
    console.log(res[0].data);
    setResult(res[0].data);
  };

  const onCurrentPress = (name = '上海市') => {
    cities.map(item =>
      item.data.map(val => {if (val.CITY_NAME === name) {onSelectCity(val);
          return null;
        }
      }),
    );
  };

  const renderSearchView = () => {
    return (<View style={styles.searchView}>
        <TextInput
          style={{flex: 1}}
          assignRef={c => {inputRef = c;}}
          onChangeText={onChangeText}
          returnKeyType="search"
          onSubmitEditing={() => {if (keyword) {searchCities();
            }
          }}
          onFocus={() => setIsFocused(true)}
          placeholder="输出城市名或拼音"
        />
        <TouchableOpacity onPress={() => searchSubmit(isFocused)}>
          <Text style={styles.searchTxt}>{!isFocused ? '搜寻' : '勾销'}</Text>
        </TouchableOpacity>
      </View>
    );
  };

  return (<View style={styles.container}>
      <Header title={'抉择城市'} />
      {renderSearchView()}
      <View style={{flex: 1}}>
        {(!isFocused && !keyword && keyword.length < 1) || !isFocused ? (
          <CityList
            onCurrentCityPress={onCurrentPress}
            onSelectCity={onSelectCity}
            currentCity={location}
            allCityList={cities}
            currentCityList={currentCityList}
          />
        ) : (<SearchResult list={result} onSelectCity={onSelectCity} />
        )}
      </View>
    </View>
  );
};

const SearchResult = ({list, onSelectCity}) => {
  return (<View style={{marginTop: 10}}>
      {list.map((item, index) => (
        <TouchableOpacity
          activeOpacity={0.7}
          key={index.toString()}
          style={styles.rowView}
          onPress={() => {onSelectCity(item);
          }}>
          <View style={styles.rowdata}>
            <Text type="subheading">{item.CITY_NAME}</Text>
          </View>
        </TouchableOpacity>
      ))}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
  },
  searchView: {
    height: 48,
    flexDirection: 'row',
    backgroundColor: '#fff',
    alignItems: 'center',
    paddingLeft: 10,
    paddingRight: 10,
  },
  searchTxt: {
    color: '#FC5869',
    marginRight: 5,
    fontSize: 16,
  },
  rowView: {
    backgroundColor: '#fff',
    height: 44,
    paddingLeft: 13,
    justifyContent: 'center',
  },
  leftIcon: {
    width: 28,
    height: 28,
    paddingLeft: 13,
  },
});

CitySelectScreen.propTypes = {
  cities: PropTypes.array,
  getCities: PropTypes.func,
};

export default CitySelectScreen;

CityList.js 代码如下:

import React, {useEffect, useRef} from 'react';
import {
  View,
  SectionList,
  TouchableOpacity,
  StyleSheet,
  Text,
  Image,
  Dimensions,
} from 'react-native';
import PropTypes from 'prop-types';
import ItemSeparatorComponent from '../../../../common/ItemSeparator';
import location from '../../../../assets/images/home/location.png';
import refresh from '../../../../assets/images/home/refresh.png';

const {width} = Dimensions.get('window');

const propTypes = {
  keyword: PropTypes.string,
  onChangeTextKeyword: PropTypes.func,
};

const defaultProps = {};

const CityList = ({
  onSelectCity,
  allCityList = [],
  currentCity,
  onCurrentCityPress,
  position: _position,
}) => {const listViewRef = useRef(null);

  useEffect(() => {console.log(allCityList);
  }, []);

  const city =
    currentCity && currentCity.city
      ? currentCity.city
      : '定位失败,请手动抉择城市';

  const _cityNameClick = cityJson => {onSelectCity(cityJson);
  };

  const getLocation = async () => {
    // await PermissionsAndroid.request(
    //   PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
    // );
    // const res = await Geocode.reverse({
    //   latitude: '29.604451007313266',
    //   longitude: '106.52727499999997',
    // });
    // _position(res);
  };

  const CityHeader = props => {const {currentCity = '上海', onCurrentCityPress} = props;
    return (
      <View>
        <View style={styles.sectionTitle}>
          <Text style={{fontSize: 15}}> 以后城市 </Text>
        </View>
        <TouchableOpacity
          activeOpacity={0.7}
          onPress={() => onCurrentCityPress(currentCity)}
          style={styles.headerContainer}>
          <View style={styles.headerLeft}>
            <Image source={location} style={{width: 20, height: 20}} />
            <Text type="subheading" style={{marginLeft: 2}}>
              {currentCity}
            </Text>
          </View>
          <TouchableOpacity activeOpacity={0.7} onPress={() => getLocation()}>
            <Image source={refresh} style={{width: 20, height: 20}} />
          </TouchableOpacity>
        </TouchableOpacity>
      </View>
    );
  };

  const _renderListRow = (cityJson, rowId) => {
    return (
      <TouchableOpacity
        activeOpacity={0.7}
        key={`list_item_${cityJson.item.CITI_CD}`}
        style={styles.rowView}
        onPress={() => _cityNameClick(cityJson.item)}>
        <View style={styles.rowData}>
          <Text type="subheading">{cityJson.item.CITY_NAME}</Text>
        </View>
      </TouchableOpacity>
    );
  };

  const _scrollTo = (index, letter) => {listViewRef?.current?.scrollToLocation({itemIndex: 0, sectionIndex: index});
  };

  const _renderRightLetters = (letter, index) => {
    return (
      <TouchableOpacity
        key={`letter_idx_${index}`}
        activeOpacity={0.6}
        onPress={() => {_scrollTo(index, letter);
        }}>
        <View style={styles.letter}>
          <Text>{letter}</Text>
        </View>
      </TouchableOpacity>
    );
  };

  return (<View style={styles.container}>
      <SectionList
        getItemLayout={(param, index) => ({
          length: 44,
          offset: 44 * index,
          index,
        })}
        ListHeaderComponent={
          <CityHeader
            currentCity={city}
            onCurrentCityPress={onCurrentCityPress}
          />
        }
        ref={listViewRef}
        sections={allCityList}
        keyExtractor={(item, index) => index.toString()}
        renderItem={_renderListRow}
        ItemSeparatorComponent={() => <ItemSeparatorComponent />}
        renderSectionHeader={({section: {name}}) => (<View style={styles.sectionTitle}>
            <Text style={{fontSize: 15}}>{name}</Text>
          </View>
        )}
        stickySectionHeadersEnabled={true}
      />
      <View style={styles.letterSpace}>
        {allCityList.map((item, index) =>
          _renderRightLetters(item.name, index),
        )}
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F4F4F4',
  },
  sectionTitle: {
    paddingVertical: 5,
    paddingLeft: 12,
    backgroundColor: '#F3F4F5',
  },
  iconContainer: {
    height: 40,
    justifyContent: 'center',
    alignItems: 'center',
  },
  leftIconContainer: {marginEnd: 12,},
  rightIconContainer: {marginStart: 8,},
  headerView: {
    width: width,
    display: 'flex',
    flexDirection: 'row',
    position: 'relative',
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  leftIcon: {
    width: 15,
    height: 13,
    marginLeft: 15,
    marginRight: 5,
  },
  rowView: {
    paddingLeft: 12,
    backgroundColor: '#fff',
  },
  rowData: {
    width: width,
    height: 44,
    justifyContent: 'center',
  },
  headerContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    backgroundColor: '#fff',
    paddingHorizontal: 12,
    height: 44,
  },
  headerLeft: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  letter: {marginBottom: 3,},
  letterSpace: {
    position: 'absolute',
    right: 4,
    bottom: 0,
    top: 0,
    justifyContent: 'center',
  },
});

CityList.propTypes = propTypes;
CityList.defaultProps = defaultProps;

export default CityList;

另外,咱们的网络申请应用的是 Axios,相干内容能够查看我之前文章的介绍:React Native 应用 axios 进行网络申请

源码:https://github.com/xiangzhihong/rn_city

退出移动版