04-HK 自定义城市选择列表
本文是 hkzf 移动端 的系列教程,旨在通过一系列的文章来帮助初学者快速掌握基于React技术栈的项目开发。
需求分析
自定义城市列表分成了左右两部分。左边分成三个部分,分别是当前定位,热门城市,以及按照A,B,C,D....字母顺序排列的的城市列表。左边列表滚动,右边字母索引跟着滚动;右边字母索引被点击,左边城市列表滚动到对应的字母索引处。点击列表,选中城市。
实现
构造需要的城市数据结构列表
[{"当前定位":""},{"热门城市":["广州","北京"..]},{A:[....]}]
getAllCities = async (params) => {
//当前定位城市
const { mapReducer } = store.getState();
const cityName = mapReducer.cityName;
// 获取所有城市
let allCities = (await axios.get("/area/city?level=1")).body;
//获取热门城市
const hotCities = (await axios.get("/area/hot")).body;
let totalCity = [
{ "当前定位": [cityName] },
{ "热门城市": hotCities.map(v => v.label) }
];
// 思考一下,还差那些内容 allCities A:[....]
//[{"当前定位":""},{"热门城市":["广州","北京"..]},{A:[....]}]
//先做一个排序 ab ac ad ba bb ...
allCities = allCities.sort(function (a, b) {
return a.short.localeCompare(b.short);
})
//{A:[....],B:[....],C:[...]}
allCities.forEach(v => {
//取到简称里面首字母的大写
let firstLetter = v.short[0].toUpperCase();
//判断首字母是否在totalCity里面已经出现
let index = totalCity.findIndex(item => {
if (item[firstLetter]) {
return true;
} else {
return false;
}
})
if (index === -1) {
//例如没有找到A [{},{"A":[v.label]}]
totalCity.push(
{ [firstLetter]: [v.label] }
);
} else {
totalCity[index][firstLetter].push(v.label)
}
});
let keyArr = totalCity.map(v => Object.keys(v)[0]);
keyArr[0] = "#";
keyArr[1] = "热";
this.setState({
totalCity, keyArr
})
}
使用 react-virtualized
进行长列表的渲染
安装 react-virtualized
npm install react-virtualized
城市列表
<div className="list_content" >
<AutoSizer>
{({ height, width }) => (
<List
ref={this.MainList} //非受控表单
height={height}
rowCount={this.state.totalCity.length}
rowHeight={this.rowHeight}
rowRenderer={this.rowRenderer}
onRowsRendered={this.onRowsRendered}
width={width}
scrollToAlignment="start" // 对齐方式, 不加的话 点击右侧的字母,左侧 列表 滚动的位置不对
/>
)}
</AutoSizer>
</div>
渲染右侧字母列表
{/* 首字母缩写开始 */}
<div className="key_list">
{
this.state.keyArr.map( (v,i) => {
return <div
onClick={this.onKeyLetterClick.bind(this,i)}
className={"key_item " + ( i===this.state.selectIndex ? "active":"")} key={v}>
{v}</div>
})
}
</div>
{/* 首字母缩写结束 */}
每一行的渲染和点击事件
rowRenderer = ({ key, index, style }) => {
//{"热门城市":["广州","上海"]}
let item = this.state.totalCity[index];
let item_name = Object.keys(item)[0];
return (
<div key={key} style={style}>
<div className="city_list_name">
{item_name}
</div>
<div className="city_list_content" >
{
item[item_name].map((v, i) => {
return <div key={i} onClick={this.itemOnClick.bind(this, v)} className="list_item">{v}</div>
})
}
</div>
</div>
);
}
itemOnClick = (v) => {
store.dispatch(getCityNameAction(v));
window.history.go(-1);
}
rowHeight = ({ index }) => {
let item = this.state.totalCity[index];
return (Object.values(item)[0].length + 1) * 40;
}
onKeyLetterClick = (index) => {
this.MainList.current.scrollToRow(index);
this.setState({
selectIndex:index
})
}
onRowsRendered = ({startIndex}) => {
this.setState({
selectIndex:startIndex
})
}