This article is the eleventh of a series of articles that explain in-depth ahoos source code, which has been organized into document- address . I think it's not bad, give a star to support it, thanks.
This article talks about useUrlState in ahooks.
Manage state Hooks through url query.
Special for useUrlState
In the previous architecture chapter, we mentioned that the ahooks project is a monoRepo
. Its project management is managed through lerna . You can see from the official website and source code that useUrlState is managed by an independent warehouse.
That is you have to install separately:
npm i @ahooksjs/use-url-state -S
I think the official reason for this is that useUrlState is based on react-router's useLocation & useHistory & useNavigate for query management. So you have to install the 5.x or 6.x version of react-router. But in fact, many React projects do not necessarily use react-router. If this hook is built into ahooks, it may cause the package size to become larger.
In addition, the hook is dependent on the query-string npm package. Using this package, I think the reasons are the following:
- First, it has powerful functions and supports many options to meet our various business needs.
- Second, it is also relatively mature in the industry, avoiding repeated wheel building.
- Thirdly, its bag is also small in size and has no burden. We mainly use its parse and stringify methods, which are only 2.4k after compression.
Briefly introduce these two methods by example:
qs.parse(string, [options])
qs.parse('?name=jim') // {name: 'jim'}
qs.parse('#token=123') // {token: '123'}
qs.parse('name=jim&name=lily&age=22') // {name: ['jim', 'lily'], age: 22}
qs.parse('foo[]=1&foo[]=2&foo[]=3', {arrayFormat: 'bracket'});
//=> {foo: ['1', '2', '3']}
qs.stringify(object, [options])
qs.stringify({name: 'jim', age: 22}); // 'age=22&name=jim'
qs.stringify({name: ['jim', 'lily'], age: 22}); // 'age=22&name=jim&name=lily'
qs.stringify({foo: [1, 2, 3]}, {arrayFormat: 'bracket'});
//=> 'foo[]=1&foo[]=2&foo[]=3'
useUrlState source code analysis
Looking directly at the code, the initial value section is displayed.
- The first parameter is the initial state, and the second parameter is the configuration of the url, including the way to switch history when the state changes, and the configuration of query-string parse and stringify.
- Get the location object of the URL through useLocation of react-router.
- react-router is compatible with 5.x and 6.x, where 5.x uses useHistory and 6.x uses useNavigate .
- queryFromUrl is to call the parse method of query-string to process the search of the location object into an object value.
- targetQuery is the final value after processing - the result of merging queryFromUrl with the initial value.
// ...
import * as tmp from 'react-router';
// ...
const useUrlState = <S extends UrlState = UrlState>(
// 初始状态
initialState?: S | (() => S),
// url 配置
options?: Options,
) => {
type State = Partial<{ [key in keyof S]: any }>;
const {
// 状态变更时切换 history 的方式
navigateMode = 'push',
// query-string parse 的配置
parseOptions,
// query-string stringify 的配置
stringifyOptions,
} = options || {};
const mergedParseOptions = { ...baseParseConfig, ...parseOptions };
const mergedStringifyOptions = { ...baseStringifyConfig, ...stringifyOptions };
// useLocation钩子返回表示当前URL的location对象。您可以将它想象成一个useState,它在URL更改时返回一个新值。
const location = rc.useLocation();
// https://v5.reactrouter.com/web/api/Hooks/usehistory
// useHistory 钩子可以访问用来导航的历史实例。
// react-router v5
const history = rc.useHistory?.();
// react-router v6
const navigate = rc.useNavigate?.();
const update = useUpdate();
const initialStateRef = useRef(
typeof initialState === 'function' ? (initialState as () => S)() : initialState || {},
);
// 根据 url query
const queryFromUrl = useMemo(() => {
return parse(location.search, mergedParseOptions);
}, [location.search]);
const targetQuery: State = useMemo(
() => ({
...initialStateRef.current,
...queryFromUrl,
}),
[queryFromUrl],
);
// 省略部分代码
}
Next, look at the status settings of the url:
- The first is to obtain a new state newQuery according to the incoming s value, which supports the function method.
Call different methods to update according to different versions of react-router.
- If it is version 5.x, call the useHistory method to update the corresponding state.
- If it is version 6.x, call the useNavigate method to update the corresponding state.
Update the status via update()
useUpdate()
.// 设置 url 状态 const setState = (s: React.SetStateAction<State>) => { const newQuery = typeof s === 'function' ? s(targetQuery) : s; // 1. 如果 setState 后,search 没变化,就需要 update 来触发一次更新。比如 demo1 直接点击 clear,就需要 update 来触发更新。 // 2. update 和 history 的更新会合并,不会造成多次更新 update(); if (history) { history[navigateMode]({ hash: location.hash, search: stringify({ ...queryFromUrl, ...newQuery }, mergedStringifyOptions) || '?', }); } if (navigate) { navigate( { hash: location.hash, search: stringify({ ...queryFromUrl, ...newQuery }, mergedStringifyOptions) || '?', }, { replace: navigateMode === 'replace', }, ); } };
Thinking and Summarizing
If a tool function/hook in the tool library depends on a package that the developer may not use, and the package is relatively large, the tool function/hook can be separated into an npm package, and the developer can use it. when installing. In addition, the package management method of monoRepo can be considered to facilitate document management and some public package management.
This article has been included in the personal blog , welcome to pay attention~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。