业务中在列表页,经常会出现一种情况,列表的请求参数需要和url参数对应,基于这个需求,可以设计一个 hook 将查询参数和 state 双向同步;
思路
function useUrlQueryState(initState = {}) {
// url 参数同步到 state
const matchUrlToState = state => nextState;
// state 同步到 url 参数
const matchStateToUrl = state => nextState;
// url state: 保持 state 和 url 同步
const [urlState, setUrlState] = useState(matchUrlToState(initState));
// 同步 url 和 state
const combineUrlState = useCallback((state) => {
setUrlState(matchStateToUrl(state));
}, []);
//
return [urlState, combineUrlState];
}
matchUrlToState 和 matchStateToUrl
总体同步原则是匹配 state 和 url search params,如果能够匹配上,则需要同步:
- matchUrlToState 初始化的时候,为惰性匹配,将 url 参数同步到 state 中;
- matchStateToUrl 主动设置为积极匹配,将 state 匹配到 url 参数中;
function useUrlQueryState(initState = {}) {
// 设置地址栏
const combineUrl = url => window.history.pushState(null, null, url.toString());
// url 参数同步到 state
const matchUrlToState = state => {
const url = new URL(window.location.href);
const nextState = Object.keys(state).reduce((nextState, key) => {
if (url.searchParams.has(key) && url.searchParams.get(key)) {
return { ...nextState, [key]: url.searchParams.get(key) };
} else {
url.searchParams.set(key, state[key]);
return { ...nextState };
}
}, state);
combineUrl(url);
return nextState;
}
// state 同步到 url 参数
const matchStateToUrl = useCallback(state => {
const url = new URL(window.location.href);
Object.keys(state).forEach(key => {
url.searchParams.set(key, state[key]);
});
combineUrl(url);
return state;
}, []);
// url state: 保持 state 和 url 同步
const [urlState, setUrlState] = useState(matchUrlToState(initState));
// 同步 url 和 state
const combineUrlState = useCallback((state) => {
setUrlState(matchStateToUrl(state));
}, [matchStateToUrl]);
return [urlState, combineUrlState];
}