作者:林朝挺
背景
在前端开发的内容中,对于一些筛选项+列表的页面经常会遇见。而往往这类页面都需要一些“智能性”,具体体现在:
- 用户对筛选条件进行筛选后,进入列表详情页,再进行返回操作,列表页还能保留之前的筛选条件。
- 用户对筛选条件进行筛选后,切换到其他平级页面或者对页面进行刷新,列表页面的筛选条件能被重置。
实现思路
从整理后的需求出发,不难发现一个点,我们需要一个能记忆筛选项的地方。对于单页应用来说,存储数据无非是 store 、localStorage、sessionStorage等地方 。
function Page(){
const dispatch = useDispatch()
const {name,time,status} = useSelector(state=>state.page)
const query = useState({
name,
time,
status
})
useEffect(()=>{
const unRegister = history.listen(location=>{
const cacheTargetRoutes = [...]// 需要存储
if(!cacheTargetRoutes.includes(location.pathname)){
dispatch(resetState())
}
})
return ()=>{
unRegister()
}
},[])
return (
<div> <div className='search-btn' onClick={()=>{ dispatch(updateState(query)) }}> 搜索 </div> </div>
)
}
这里我们利用store存储了列表页的筛选项。但是此处的记忆值数据与UI的更新并无直接关系,反而使得应用变得复杂。我们可以利用自定义的hooks来改进这种方案。具体实现如下:
import { useCallback, useEffect, useRef, useState } from 'react'
type Options = {
auto: boolean
}
type Key = string | symbol | number
const defaultOption: Options = {
auto: true
}
const map = new Map<Key, any>()
function useCacheState<S = any>(key: Key, initialState: S, ops?: Options) {
const { auto } = Object.assign(defaultOption, ops || {})
const [state, setState] = useState<S>(
map.has(key) ? map.get(key) : initialState
)
const stateRef = useRef(state)
function wrapSetState(arg: S) {
stateRef.current = arg
if (auto) {
map.set(key, arg)
}
setState(arg)
}
useEffect(() => {
if (auto) {
map.set(key, state)
}
}, [key, state, auto])
const update = useCallback(() => {
map.set(key, stateRef.current)
}, [key, stateRef])
const clear = useCallback(() => {
map.delete(key)
}, [key])
return [state, wrapSetState, clear, update] as const
}
export default useCacheState
至此,一个带有记忆功能的 useState 便完成了