点击弹出弹窗
- citySelect.tsx
const CitySelect: React.FunctionComponent = (props) => {
const [dialogVisible, setDialogVisible] = useState(false)
const onClick = () => {
setDialogVisible(true)
}
const onClose = () => {
setDialogVisible(false)
}
return (
<>
<div onClick={onClick}>{props.children}</div>
{dialogVisible && <Dialog onClose={onClose}/>}
</>
)
}
const Dialog: React.FC<{onClose: () => void}> = (props) => {
return ReactDOM.createPortal((
<div className={"ireact-citySelect-dialog"} onClick={props.onClose}>
弹出内容
</div>
), document.body)
}
查询当前位置模块
const Dialog: React.FC<{onClose: () => void}> = (props) => {
return ReactDOM.createPortal((
<div className={"ireact-citySelect-dialog"} onClick={props.onClose}>
<header>
<Icon name={"left"}/>
<span>选择城市</span>
</header>
<CurrentLocation/>
<h2>全部城市</h2>
<div className="cityIndex">ABCD...</div>
<div className="cityList">所有城市</div>
</div>
), document.body)
}
const CurrentLocation: React.FC = () => {
const [city, setCity] = useState<string>('加载中...')
useEffect(() => {
const xhr = new XMLHttpRequest()
xhr.open('get', 'http://ip-api.com/json/?lang=zh-CN')
xhr.onload = () => {
const string = xhr.responseText
const obj = JSON.parse(string)
const C = obj.city
setCity(C)
}
xhr.send()
xhr.onerror = () => {
setCity('未知')
}
}, [])
return (
<div className={"currentCity"}>
{city}
</div>
)
}
数据列表处理
这里我们使用写死的城市数据,然后引入一个tiny-pinyin库来讲城市的首字母取出来
import pinyin from 'tiny-pinyin'
interface Prop {
dataSource: string[]
}
const CitySelect: React.FunctionComponent<Prop> = (props) => {
const lists: {[key: string]: string[]} = {}
props.dataSource.map((city) => {
const py = pinyin.convertToPinyin(city)
const index = py[0]
lists[index] = lists[index] || []
lists[index].push(city)
})
}
使用Context将城市数据传给dialog
interface Context {
lists: {[key: string]: string[]}
}
const CityContext = createContext<Context>({lists: {a: []}})
const CitySelect: React.FunctionComponent<Prop> = (props) => {
const lists: Context['lists'] = {}
return (
<CityContext.Provider value={{lists}}>
<div onClick={onClick}>{props.children}</div>
{dialogVisible && <Dialog onClose={onClose}/>}
</CityContext.Provider>
)
}
const Dialog: React.FC<{onClose: () => void}> = (props) => {
const {lists} = useContext(CityContext)
const indexList = Object.keys(lists).sort()
return ReactDOM.createPortal((
<ol className="ireact-citySelect-index">
{indexList.map(a => <li key={a}>{a}</li>)}
</ol>
))
渲染字母和对应城市
将之前的lists对象里的key和value结构转换成数组结构,数组里面的每一项还是数组,第一项是key,第二项是对应的value,我们可以使用Object.entries()
// 转换后根据第一项也就是字母进行排序每一项,因为每一项的第零项都是字符串
// 所以需要用charCodeAt转换成number
const cityList = Object.entries(lists).sort(
(a, b) => a[0].charCodeAt(0) - b[0].charCodeAt(0)
)
<div className="cityList">
{cityList.map(([letter, list]) =>
<div key={letter} className={"ireact-citySelect-cityList"}>
<h4>{letter}</h4>
<ol>
{list.map(city =>
<li key={city}>{city}</li>
)}
</ol>
</div>
)}
</div>
网友评论