Although React Native is no longer a popular cross-platform technology, you can still see that many companies and individuals are using it. Flutter is good for Flutter, and RN also has its own place. As for which is better, we have not done it. Many comparisons.
Recently, I upgraded the book "React Native Mobile Development Practical Combat" before the upgrade. The projects in the book have the function of city switching, and the effect is shown in the figure below.
As you can see, this city selection page is very conventional, including the current location city and city list. On the right side, you can quickly locate it through the SlideBar. In addition, this component also supports the search function.
First, let's take a look at the city list. For this function, we can use the SectionList component, because we can use the ListHeaderComponent property of the SectionList to implement the current positioning layout, and the letter index effect on the right needs to use the scrollToLocation() function of the SectionList, as shown below Show.
const _scrollTo = (index, letter) => {
listViewRef?.current?.scrollToLocation({itemIndex: 0, sectionIndex: index});
};
The search function is even simpler, just use the FlatList component to display, here you can also use the list.map loop to develop the list function.
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;
The CityList.js code is as follows:
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;
In addition, our network request uses Axios. For related content, please refer to the introduction of my previous article: React Native uses axios for network requests
Source code: https://github.com/xiangzhihong/rn_city
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。