clear-react-router 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/dist/hooks/useSearchParams.d.ts +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +48 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -272,6 +272,53 @@ const { setContext, context } = useRouterContext();
|
|
|
272
272
|
const loginHandler = () => setContext({ ...context, user: { name: 'John' } });
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
+
### `useSearchParams()`
|
|
276
|
+
|
|
277
|
+
Returns an object for working with URL query parameters. Supports reading and setting both single values and arrays.
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
import { useSearchParams } from 'clear-react-router';
|
|
281
|
+
|
|
282
|
+
function ProductFilter() {
|
|
283
|
+
const { searchParams, getSearchParams, setSearchParams } = useSearchParams();
|
|
284
|
+
|
|
285
|
+
// Get a single value or array
|
|
286
|
+
const brand = getSearchParams('brand'); // 'nike' | ['nike', 'reebok'] | ''
|
|
287
|
+
|
|
288
|
+
// Set a single value
|
|
289
|
+
setSearchParams('brand', 'nike'); // ?brand=nike
|
|
290
|
+
|
|
291
|
+
// Set multiple values (array)
|
|
292
|
+
setSearchParams('brand', ['nike', 'reebok']); // ?brand=nike&brand=reebok
|
|
293
|
+
|
|
294
|
+
// Functional update (preserves other params)
|
|
295
|
+
setSearchParams((prev) => {
|
|
296
|
+
prev.set('page', '2');
|
|
297
|
+
prev.append('color', 'red');
|
|
298
|
+
return prev;
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Direct access to URLSearchParams
|
|
302
|
+
const allParams = searchParams.toString(); // "brand=nike&brand=reebok&page=2"
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
**Returns:**
|
|
306
|
+
|
|
307
|
+
| Property | Type | Description |
|
|
308
|
+
|----------|------|-------------|
|
|
309
|
+
| `searchParams` | `URLSearchParams` | Raw `URLSearchParams` object for low-level access |
|
|
310
|
+
| `getSearchParams` | `(key: string) => string \| string[]` | Returns a single value or an array if multiple values exist for the key |
|
|
311
|
+
| `setSearchParams` | `(param: string, value: string \| string[]) => void` `or` `(updater: (prev: URLSearchParams) => URLSearchParams) => void` | Update query parameters. Supports single values, arrays, or functional updates |
|
|
312
|
+
|
|
313
|
+
**Key features:**
|
|
314
|
+
|
|
315
|
+
- ✅ **Array support** — `getSearchParams` returns `string[]` when multiple values exist for the same key
|
|
316
|
+
- ✅ **Functional updates** — Update parameters based on previous state without losing other params
|
|
317
|
+
- ✅ **Type-safe** — Proper TypeScript support with overloads
|
|
318
|
+
- ✅ **Stable reference** — `setSearchParams` reference is stable and safe to use in `useEffect`
|
|
319
|
+
|
|
320
|
+
> **Note:** `getSearchParams` returns `string` for single values, `string[]` for multiple values, and `''` if the key is not found.
|
|
321
|
+
|
|
275
322
|
## Lazy Loading
|
|
276
323
|
|
|
277
324
|
Clear Router supports code-splitting out of the box. Simply pass a function that returns a dynamic import:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type UseSearchParamsReturn = {
|
|
2
|
+
searchParams: URLSearchParams;
|
|
3
|
+
getSearchParams(arg: string): string | string[];
|
|
4
|
+
setSearchParams: {
|
|
5
|
+
(param: string, value: string | string[]): void;
|
|
6
|
+
(param: (prevState: URLSearchParams) => URLSearchParams): void;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export declare const useSearchParams: () => UseSearchParamsReturn;
|
|
10
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,6 @@ export { useLoaderState } from './hooks/useLoaderState';
|
|
|
8
8
|
export { useBlocker } from './hooks/useBlocker';
|
|
9
9
|
export { useBeforeUnload } from './hooks/useBeforeUnload';
|
|
10
10
|
export { useRouterContext } from './hooks/useRouterContext';
|
|
11
|
+
export { useSearchParams } from './hooks/useSearchParams';
|
|
11
12
|
export { createRouter } from './utils/utils';
|
|
12
13
|
export type { RouteItem, BlockerState, Location } from './types/global';
|
package/dist/index.js
CHANGED
|
@@ -149,8 +149,9 @@ var useHandleNavigation = ({ setLocation, routeList, context, revalidateCache, i
|
|
|
149
149
|
const navigation = useCallback((nextLocation) => {
|
|
150
150
|
setLocation(nextLocation);
|
|
151
151
|
prevPathname.current = nextLocation.pathname;
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
const fullPath = nextLocation.search ? `${nextLocation.pathname}${nextLocation.search}` : nextLocation.pathname;
|
|
153
|
+
if (fullPath === window.location.pathname + window.location.search) return;
|
|
154
|
+
history.pushState(null, "", fullPath);
|
|
154
155
|
}, [setLocation]);
|
|
155
156
|
const transitionedNavigation = useCallback(({ nextLocation, isFirstCall, isAnimated }) => {
|
|
156
157
|
if (isAnimated && !isFirstCall) try {
|
|
@@ -427,26 +428,15 @@ var renderElement = (Component) => {
|
|
|
427
428
|
};
|
|
428
429
|
//#endregion
|
|
429
430
|
//#region hooks/useServiceContext.ts
|
|
430
|
-
var
|
|
431
|
-
const context = useContext(
|
|
432
|
-
if (!Object.keys(context).length) throw new Error("hooks and Router component must be used within RouterProvider");
|
|
433
|
-
return context;
|
|
434
|
-
};
|
|
435
|
-
var useRouterActions = () => {
|
|
436
|
-
const context = useContext(ActionsContext);
|
|
437
|
-
if (!Object.keys(context).length) throw new Error("hooks and Router component must be used within RouterProvider");
|
|
438
|
-
return context;
|
|
439
|
-
};
|
|
440
|
-
var useRouterData = () => {
|
|
441
|
-
const context = useContext(DataContext);
|
|
442
|
-
if (!Object.keys(context).length) throw new Error("hooks and Router component must be used within RouterProvider");
|
|
443
|
-
return context;
|
|
444
|
-
};
|
|
445
|
-
var usePropsData = () => {
|
|
446
|
-
const context = useContext(PropsContext);
|
|
431
|
+
var useServiceState = (reactContext) => {
|
|
432
|
+
const context = useContext(reactContext);
|
|
447
433
|
if (!Object.keys(context).length) throw new Error("hooks and Router component must be used within RouterProvider");
|
|
448
434
|
return context;
|
|
449
435
|
};
|
|
436
|
+
var useNavigationState = () => useServiceState(NavigationContext);
|
|
437
|
+
var useRouterActions = () => useServiceState(ActionsContext);
|
|
438
|
+
var useRouterData = () => useServiceState(DataContext);
|
|
439
|
+
var usePropsData = () => useServiceState(PropsContext);
|
|
450
440
|
//#endregion
|
|
451
441
|
//#region context/RouterViewContext.ts
|
|
452
442
|
var RouterViewContext = createContext({});
|
|
@@ -501,13 +491,13 @@ var useLocation = () => {
|
|
|
501
491
|
//#region hooks/useNavigate.ts
|
|
502
492
|
var useNavigate = () => {
|
|
503
493
|
const { updateLocation } = useRouterActions();
|
|
504
|
-
const
|
|
494
|
+
const locationRef = useLatest(useLocation());
|
|
505
495
|
return useCallback(async (arg) => {
|
|
506
496
|
if (typeof arg === "number") return history.go(arg);
|
|
507
497
|
if (typeof arg === "string") {
|
|
508
|
-
if (arg !==
|
|
509
|
-
} else if (JSON.stringify(arg) !== JSON.stringify(
|
|
510
|
-
}, [
|
|
498
|
+
if (arg !== locationRef.current.pathname) await updateLocation({ pathname: arg });
|
|
499
|
+
} else if (JSON.stringify(arg) !== JSON.stringify(locationRef.current)) await updateLocation(arg);
|
|
500
|
+
}, [updateLocation, locationRef]);
|
|
511
501
|
};
|
|
512
502
|
//#endregion
|
|
513
503
|
//#region components/Link.tsx
|
|
@@ -576,4 +566,38 @@ var useRouterContext = () => {
|
|
|
576
566
|
};
|
|
577
567
|
};
|
|
578
568
|
//#endregion
|
|
579
|
-
|
|
569
|
+
//#region hooks/useSearchParams.ts
|
|
570
|
+
var useSearchParams = () => {
|
|
571
|
+
const location = useLocation();
|
|
572
|
+
const navigate = useNavigate();
|
|
573
|
+
const locationRef = useLatest(location);
|
|
574
|
+
const searchString = useMemo(() => location.search ? location.search.replace("?", "") : location.pathname.split("?")?.[1] ?? "", [location.pathname, location.search]);
|
|
575
|
+
const searchParams = useMemo(() => new URLSearchParams(searchString), [searchString]);
|
|
576
|
+
const getSearchParams = useCallback((param) => {
|
|
577
|
+
const allValues = searchParams.getAll(param);
|
|
578
|
+
return allValues.length > 1 ? allValues : allValues[0] ?? "";
|
|
579
|
+
}, [searchParams]);
|
|
580
|
+
const navigateWithSearchParams = useCallback((params) => {
|
|
581
|
+
const newSearch = params.toString();
|
|
582
|
+
navigate({
|
|
583
|
+
pathname: locationRef.current.pathname,
|
|
584
|
+
search: newSearch ? `?${newSearch}` : ""
|
|
585
|
+
});
|
|
586
|
+
}, [locationRef, navigate]);
|
|
587
|
+
return {
|
|
588
|
+
searchParams,
|
|
589
|
+
getSearchParams,
|
|
590
|
+
setSearchParams: useCallback((param, value) => {
|
|
591
|
+
const search = locationRef.current.search || "";
|
|
592
|
+
const currentParams = new URLSearchParams(search);
|
|
593
|
+
if (typeof param === "string" && value !== void 0) {
|
|
594
|
+
currentParams.delete(param);
|
|
595
|
+
(Array.isArray(value) ? value : [value]).forEach((v) => currentParams.append(param, v));
|
|
596
|
+
navigateWithSearchParams(currentParams);
|
|
597
|
+
} else if (typeof param === "function") navigateWithSearchParams(param(currentParams));
|
|
598
|
+
else throw new Error("useSearchParams first argument must be either function or string");
|
|
599
|
+
}, [locationRef, navigateWithSearchParams])
|
|
600
|
+
};
|
|
601
|
+
};
|
|
602
|
+
//#endregion
|
|
603
|
+
export { Link, Router, RouterProvider, createRouter, useBeforeUnload, useBlocker, useLoaderState, useLocation, useNavigate, useParams, useRouterContext, useSearchParams };
|