Skip to content

Commit

Permalink
feat: useUrlState can handle multiple useUrlState updates simultaneously
Browse files Browse the repository at this point in the history
  • Loading branch information
miracles1919 authored and 51wangping committed Mar 17, 2023
1 parent bafd70d commit 39d75cd
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 43 deletions.
7 changes: 7 additions & 0 deletions packages/hooks/src/useRequest/src/useRequestImplement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import useMemoizedFn from '../../useMemoizedFn';
import useMount from '../../useMount';
import useUnmount from '../../useUnmount';
import useUpdate from '../../useUpdate';
import isDev from '../../utils/isDev';

import Fetch from './Fetch';
import type { Options, Plugin, Result, Service } from './types';
Expand All @@ -15,6 +16,12 @@ function useRequestImplement<TData, TParams extends any[]>(
) {
const { manual = false, ...rest } = options;

if (isDev) {
if (options.defaultParams && !Array.isArray(options.defaultParams)) {
console.warn(`expected defaultParams is array, got ${typeof options.defaultParams}`);
}
}

const fetchOptions = {
manual,
...rest,
Expand Down
85 changes: 85 additions & 0 deletions packages/use-url-state/demo/demo4.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* title: Multi-state management (split)
* desc: useUrlState can handle multiple useUrlState updates simultaneously
*
* title.zh-CN: 多状态状态 (拆分)
* desc.zh-CN: useUrlState 可以同时处理多个 useUrlState 更新
*/

import React from 'react';
import useUrlState from '@ahooksjs/use-url-state';

export default () => {
const [page, setPage] = useUrlState({ page: '1' });
const [pageSize, setPageSize] = useUrlState({ pageSize: '10' });

return (
<>
<div>
page: {page.page}
<span style={{ paddingLeft: 8 }}>
<button
onClick={() => {
setPage((s) => ({ page: Number(s.page) + 1 }));
}}
>
+
</button>
<button
onClick={() => {
setPage((s) => ({ page: Number(s.page) - 1 }));
}}
style={{ margin: '0 8px' }}
>
-
</button>
<button
onClick={() => {
setPage({ page: undefined });
}}
>
reset
</button>
</span>
</div>
<br />
<div>
pageSize: {pageSize.pageSize}
<span style={{ paddingLeft: 8 }}>
<button
onClick={() => {
setPageSize((s) => ({ pageSize: Number(s.pageSize) + 1 }));
}}
>
+
</button>
<button
onClick={() => {
setPageSize((s) => ({ pageSize: Number(s.pageSize) - 1 }));
}}
style={{ margin: '0 8px' }}
>
-
</button>
<button
onClick={() => {
setPageSize({ pageSize: undefined });
}}
>
reset
</button>
</span>
</div>
<div>
<button
onClick={async () => {
await setPageSize({ pageSize: undefined });
await setPage({ page: undefined });
}}
>
reset all
</button>
</div>
</>
);
};
65 changes: 34 additions & 31 deletions packages/use-url-state/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type * as React from 'react';
import * as tmp from 'react-router';

// ignore waring `"export 'useNavigate' (imported as 'rc') was not found in 'react-router'`
const rc = tmp as any;
const reactRouter = tmp as any;

export interface Options {
navigateMode?: 'push' | 'replace';
Expand Down Expand Up @@ -36,12 +36,12 @@ const useUrlState = <S extends UrlState = UrlState>(
const mergedParseOptions = { ...baseParseConfig, ...parseOptions };
const mergedStringifyOptions = { ...baseStringifyConfig, ...stringifyOptions };

const location = rc.useLocation();
const location = reactRouter.useLocation();

// react-router v5
const history = rc.useHistory?.();
const history = reactRouter.useHistory?.();
// react-router v6
const navigate = rc.useNavigate?.();
const navigate = reactRouter.useNavigate?.();

const update = useUpdate();

Expand All @@ -61,33 +61,36 @@ const useUrlState = <S extends UrlState = UrlState>(
[queryFromUrl],
);

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) || '?',
},
location.state,
);
}
if (navigate) {
navigate(
{
hash: location.hash,
search: stringify({ ...queryFromUrl, ...newQuery }, mergedStringifyOptions) || '?',
},
{
replace: navigateMode === 'replace',
state: location.state,
},
);
}
const setState = (s: React.SetStateAction<State>): Promise<void> => {
return new Promise((resolve) => {
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) || '?',
},
location.state,
);
}
if (navigate) {
navigate(
{
hash: location.hash,
search: stringify({ ...queryFromUrl, ...newQuery }, mergedStringifyOptions) || '?',
},
{
replace: navigateMode === 'replace',
state: location.state,
},
);
}
resolve();
});
};

return [targetQuery, useMemoizedFn(setState)] as const;
Expand Down
19 changes: 11 additions & 8 deletions packages/use-url-state/use-url-state.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ npm i @ahooksjs/use-url-state -S
>
> 2\. Installed @ahooksjs/use-url-state

## Usage

```js
Expand All @@ -44,6 +43,10 @@ React Router V6:https://codesandbox.io/s/autumn-shape-odrt9?file=/App.tsx

<code src="./demo/demo2.tsx" hideActions='["CSB"]' />

### Multi-state management (split)

<code src="./demo/demo4.tsx" hideActions='["CSB"]' />

### Custom query-string options

<code src="./demo/demo3.tsx" hideActions='["CSB"]' />
Expand All @@ -57,21 +60,21 @@ const [state, setState] = useUrlState(initialState, options);
### Params

| Property | Description | Type | Default |
|--------------|--------------------------------|----------------|---------|
| ------------ | ------------------------------ | -------------- | ------- |
| initialState | InitialState, same as useState | `S \| () => S` | - |
| options | Url config | `Options` | - |

### Options

| Property | Description | Type | Default |
|------------------|--------------------------------------------------------------------------------------------------------------|-----------------------|----------|
| navigateMode | Type of history navigate mode | `'push' \| 'replace'` | `'push'` |
| parseOptions | [parse](https://github.com/sindresorhus/query-string#parsestring-options) options of `query-string` | `ParseOptions` | - |
| stringifyOptions | [stringify](https://github.com/sindresorhus/query-string#stringifyobject-options) options of `query-string` | `StringifyOptions` | - |
| Property | Description | Type | Default |
| ---------------- | ----------------------------------------------------------------------------------------------------------- | --------------------- | -------- |
| navigateMode | Type of history navigate mode | `'push' \| 'replace'` | `'push'` |
| parseOptions | [parse](https://github.com/sindresorhus/query-string#parsestring-options) options of `query-string` | `ParseOptions` | - |
| stringifyOptions | [stringify](https://github.com/sindresorhus/query-string#stringifyobject-options) options of `query-string` | `StringifyOptions` | - |

### Result

| Property | Description | Type |
|----------|----------------------------------------------|---------------------------------------------------|
| -------- | -------------------------------------------- | ------------------------------------------------- |
| state | Url query object | `object` |
| setState | Same as useState, but state should be object | `(state: S) => void \| (() => ((state: S) => S))` |
10 changes: 7 additions & 3 deletions packages/use-url-state/use-url-state.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ React Router V6:https://codesandbox.io/s/autumn-shape-odrt9?file=/App.tsx

<code src="./demo/demo2.tsx" hideActions='["CSB"]' />

### 多状态管理(拆分)

<code src="./demo/demo4.tsx" hideActions='["CSB"]' />

### 自定义 query-string 配置

<code src="./demo/demo3.tsx" hideActions='["CSB"]' />
Expand All @@ -56,21 +60,21 @@ const [state, setState] = useUrlState(initialState, options);
### Params

| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------|----------------|--------|
| ------------ | -------- | -------------- | ------ |
| initialState | 初始状态 | `S \| () => S` | - |
| options | url 配置 | `Options` | - |

### Options

| 参数 | 说明 | 类型 | 默认值 |
|------------------|---------------------------------------------------------------------------------------------------------|-----------------------|----------|
| ---------------- | ------------------------------------------------------------------------------------------------------- | --------------------- | -------- |
| navigateMode | 状态变更时切换 history 的方式 | `'push' \| 'replace'` | `'push'` |
| parseOptions | `query-string` [parse](https://github.com/sindresorhus/query-string#parsestring-options) 的配置 | `ParseOptions` | - |
| stringifyOptions | `query-string` [stringify](https://github.com/sindresorhus/query-string#stringifyobject-options) 的配置 | `StringifyOptions` | - |

### Result

| 参数 | 说明 | 类型 |
|----------|-----------------------------------------|---------------------------------------------------|
| -------- | --------------------------------------- | ------------------------------------------------- |
| state | url query 对象 | `object` |
| setState | 用法同 useState,但 state 需要是 object | `(state: S) => void \| (() => ((state: S) => S))` |
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "ES6",
"target": "ES5",
"moduleResolution": "node",
"jsx": "react",
"esModuleInterop": true,
Expand Down

0 comments on commit 39d75cd

Please sign in to comment.