state-in-url 6.5.0 → 7.0.0

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.
Files changed (37) hide show
  1. package/README.es.md +70 -5
  2. package/README.fr.md +70 -5
  3. package/README.ja.md +68 -3
  4. package/README.ko.md +68 -3
  5. package/README.md +69 -4
  6. package/README.pt-BR.md +70 -5
  7. package/README.ru.md +70 -5
  8. package/README.vi.md +70 -5
  9. package/README.zh-CN.md +70 -5
  10. package/dist/astro/index.js +1 -1
  11. package/dist/astro/index.mjs +1 -1
  12. package/dist/astro/useUrlState/index.js +1 -1
  13. package/dist/astro/useUrlState/index.mjs +1 -1
  14. package/dist/astro/useUrlState/useUrlState.d.ts +1 -85
  15. package/dist/index.d.ts +1 -2
  16. package/dist/index.js +1 -1
  17. package/dist/index.mjs +1 -1
  18. package/dist/next/useUrlState/useUrlState.mjs +1 -1
  19. package/dist/package.json +6 -1
  20. package/dist/react/index.d.ts +1 -0
  21. package/dist/react/index.js +1 -0
  22. package/dist/react/index.mjs +1 -0
  23. package/dist/react/useUrlState/index.d.ts +1 -0
  24. package/dist/react/useUrlState/index.js +1 -0
  25. package/dist/react/useUrlState/index.mjs +1 -0
  26. package/dist/react/useUrlState/useUrlState.d.ts +86 -0
  27. package/package.json +6 -1
  28. package/skills/astro-setup/SKILL.md +1 -1
  29. package/skills/feature-state-hook/SKILL.md +1 -1
  30. package/skills/form-library-integration/SKILL.md +1 -1
  31. package/skills/input-handling/SKILL.md +1 -1
  32. package/skills/nextjs-ssr/SKILL.md +1 -1
  33. package/skills/react-no-router/SKILL.md +215 -0
  34. package/skills/react-router-remix-setup/SKILL.md +1 -1
  35. package/skills/shared-state-no-url/SKILL.md +1 -1
  36. /package/dist/{astro → react}/useUrlState/useUrlState.js +0 -0
  37. /package/dist/{astro → react}/useUrlState/useUrlState.mjs +0 -0
package/README.ru.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <!-- i18n:start -->
2
2
  [English](./README.md) · [简体中文](./README.zh-CN.md) · [日本語](./README.ja.md) · [한국어](./README.ko.md) · Русский · [Español](./README.es.md) · [Português (BR)](./README.pt-BR.md) · [Français](./README.fr.md) · [Tiếng Việt](./README.vi.md)
3
- <!-- i18n:meta locale=ru source=README.md source-blob=4d98ba970e6aab8f29987fbcba29fb6c61c2fc67 status=translated -->
3
+ <!-- i18n:meta locale=ru source=README.md source-blob=ad78fdbbee096115a953813ed9a462e3393c5736 status=translated -->
4
4
  <!-- i18n:end -->
5
5
 
6
6
  <div align="center">
@@ -150,14 +150,16 @@ nuqs — достойная библиотека: берите её, если х
150
150
  - [Пример](#пример)
151
151
  - [Hook useUrlState для React-Router](#hook-useurlstate-для-react-router)
152
152
  - [Пример](#пример-1)
153
- - [Hook useUrlState для Astro](#hook-useurlstate-для-astro)
153
+ - [Hook useUrlState для React (без роутера)](#hook-useurlstate-для-react-без-роутера)
154
154
  - [Пример](#пример-2)
155
+ - [Hook useUrlState для Astro](#hook-useurlstate-для-astro)
156
+ - [Пример](#пример-3)
155
157
  - [Рецепты](#рецепты)
156
158
  - [Пользовательский hook для удобной работы со срезом состояния](#пользовательский-hook-для-удобной-работы-со-срезом-состояния)
157
159
  - [Со сложной формой состояния](#со-сложной-формой-состояния)
158
160
  - [Обновлять только состояние и синхронизировать с URL вручную](#обновлять-только-состояние-и-синхронизировать-с-url-вручную)
159
161
  - [Другие hook'и и вспомогательные функции](#другие-hookи-и-вспомогательные-функции)
160
- - [Hook `useUrlStateBase` для других роутеров](#hook-useurlstatebase-для-других-роутеров)
162
+ - [Hook `useUrlStateBase` для другого роутера](#hook-useurlstatebase-для-другого-роутера)
161
163
  - [Hook `useSharedState` для React.js](#hook-usesharedstate-для-reactjs)
162
164
  - [Hook `useLinkProps` для React.js](#hook-uselinkprops-для-reactjs)
163
165
  - [Hook `useUrlEncode` для React.js](#hook-useurlencode-для-reactjs)
@@ -540,6 +542,39 @@ const tags = [
540
542
 
541
543
  [Пример кода](packages/example-react-router6/src/Form-for-test.tsx)
542
544
 
545
+ ### Hook useUrlState для React (без роутера)
546
+
547
+ Для React-приложения без роутера — Vite, Create React App, виджет, встраиваемый в чужую страницу. Hook пишет URL через `window.history` и перечитывает его при переходах назад/вперёд и при любом другом `pushState`/`replaceState`. Все компоненты, использующие один и тот же объект состояния, разделяют его: ничего не нужно оборачивать и ничего не нужно передавать через props.
548
+
549
+ [Документация по API](packages/urlstate/react/useUrlState)
550
+
551
+ #### Пример
552
+
553
+ ```typescript
554
+ // useFilters.ts
555
+ import { useUrlState } from 'state-in-url/react';
556
+
557
+ type FiltersState = { sort: 'name' | 'date'; page: number };
558
+ const FILTERS_STATE: FiltersState = { sort: 'name', page: 1 };
559
+
560
+ export function useFilters() {
561
+ return useUrlState(FILTERS_STATE);
562
+ }
563
+ ```
564
+
565
+ ```typescript
566
+ // FiltersBar.tsx
567
+ import { useFilters } from './useFilters';
568
+
569
+ export function FiltersBar() {
570
+ const { urlState, setUrl } = useFilters();
571
+
572
+ return <button onClick={() => setUrl({ page: urlState.page + 1 })}>Page {urlState.page}</button>;
573
+ }
574
+ ```
575
+
576
+ `state-in-url/astro` — это тот же hook; ниже он описан отдельно, потому что islands дополнительно принимают `searchParams` как prop.
577
+
543
578
  ### Hook useUrlState для Astro
544
579
 
545
580
  Для React-островов. У Astro по умолчанию нет клиентского роутера, поэтому hook записывает URL через `window.history` и считывает его обратно при переходах назад/вперёд и при любом другом `pushState`/`replaceState`, включая собственный `<ClientRouter />` Astro. Острова на странице используют общее состояние, и оборачивать их ни во что не нужно.
@@ -818,9 +853,39 @@ const tags = [
818
853
 
819
854
  ## Другие hook'и и вспомогательные функции
820
855
 
821
- ### Hook `useUrlStateBase` для других роутеров
856
+ ### Hook `useUrlStateBase` для другого роутера
857
+
858
+ Используйте его, чтобы собрать собственный `useUrlState` для роутера, для которого пакет не поставляет точку входа, например TanStack Router. Передайте любой мемоизированный объект с методами `push(url)` и `replace(url)`.
859
+
860
+ Если роутера **нет вообще**, это не нужно — возьмите [`state-in-url/react`](#hook-useurlstate-для-react-без-роутера): это тот же hook, уже подключённый к `window.history`.
861
+
862
+ ```typescript
863
+ import { useUrlStateBase } from 'state-in-url/useUrlStateBase';
864
+
865
+ type FiltersState = { sort: 'name' | 'date'; page: number };
866
+ const FILTERS_STATE: FiltersState = { sort: 'name', page: 1 };
867
+
868
+ export function useFilters() {
869
+ const router = useYourRouter();
870
+ const nav = React.useMemo(
871
+ () => ({
872
+ push: (url: string) => router.navigate(url),
873
+ replace: (url: string) => router.navigate(url, { replace: true }),
874
+ }),
875
+ [router],
876
+ );
877
+
878
+ return useUrlStateBase(FILTERS_STATE, nav, ({ parse }) => parse(window.location.search));
879
+ }
880
+ ```
881
+
882
+ Два отличия от `useUrlState`:
883
+
884
+ - Он возвращает `state`, `updateUrl`, `updateState`, а не `urlState`, `setUrl`, `setState`; при этом `updateUrl` по умолчанию делает push, а `setUrl` — replace.
885
+ - Третий аргумент задаёт начальный state. Если его не передать, hook проигнорирует строку запроса, с которой была открыта страница, и присланная кем-то ссылка покажет значения по умолчанию.
822
886
 
823
- Hook'и для создания собственных hook'ов `useUrlState` с другими роутерами, например react-router или tanstack router.
887
+ > [!NOTE]
888
+ > `useUrlState` больше не экспортируется из корня пакета: каждый из них привязан к своему фреймворку, поэтому у каждого своя точка входа (`state-in-url/next`, `/react`, `/react-router`, `/react-router6`, `/remix`, `/astro`). В корне остаётся только то, что от фреймворка не зависит: `useSharedState`, `useUrlEncode`, `useUrlStateBase`, `useLinkProps`, `encode`/`decode` и `encodeState`/`decodeState`.
824
889
 
825
890
  [Документация по API](packages/urlstate/useUrlStateBase)
826
891
 
package/README.vi.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <!-- i18n:start -->
2
2
  [English](./README.md) · [简体中文](./README.zh-CN.md) · [日本語](./README.ja.md) · [한국어](./README.ko.md) · [Русский](./README.ru.md) · [Español](./README.es.md) · [Português (BR)](./README.pt-BR.md) · [Français](./README.fr.md) · Tiếng Việt
3
- <!-- i18n:meta locale=vi source=README.md source-blob=4d98ba970e6aab8f29987fbcba29fb6c61c2fc67 status=translated -->
3
+ <!-- i18n:meta locale=vi source=README.md source-blob=ad78fdbbee096115a953813ed9a462e3393c5736 status=translated -->
4
4
  <!-- i18n:end -->
5
5
 
6
6
  <div align="center">
@@ -150,14 +150,16 @@ Bản so sánh đầy đủ — cùng một tính năng trong cả hai, các l
150
150
  - [Ví dụ](#ví-dụ)
151
151
  - [Hook useUrlState cho React-Router](#hook-useurlstate-cho-react-router)
152
152
  - [Ví dụ](#ví-dụ-1)
153
- - [Hook useUrlState cho Astro](#hook-useurlstate-cho-astro)
153
+ - [Hook useUrlState cho React (không có router)](#hook-useurlstate-cho-react-không-có-router)
154
154
  - [Ví dụ](#ví-dụ-2)
155
+ - [Hook useUrlState cho Astro](#hook-useurlstate-cho-astro)
156
+ - [Ví dụ](#ví-dụ-3)
155
157
  - [Công thức nấu ăn](#công-thức-nấu-ăn)
156
158
  - [Hook tùy chỉnh để làm việc thuận tiện với một phần của state](#hook-tùy-chỉnh-để-làm-việc-thuận-tiện-với-một-phần-của-state)
157
159
  - [Với hình dạng state phức tạp](#với-hình-dạng-state-phức-tạp)
158
160
  - [Chỉ cập nhật state và đồng bộ với URL thủ công](#chỉ-cập-nhật-state-và-đồng-bộ-với-url-thủ-công)
159
161
  - [Các hook và helper khác](#các-hook-và-helper-khác)
160
- - [Hook `useUrlStateBase` cho các router khác](#hook-useurlstatebase-cho-các-router-khác)
162
+ - [Hook `useUrlStateBase` cho router khác](#hook-useurlstatebase-cho-router-khác)
161
163
  - [Hook `useSharedState` cho React.js](#hook-usesharedstate-cho-reactjs)
162
164
  - [Hook `useLinkProps` cho React.js](#hook-uselinkprops-cho-reactjs)
163
165
  - [Hook `useUrlEncode` cho React.js](#hook-useurlencode-cho-reactjs)
@@ -540,6 +542,39 @@ const tags = [
540
542
 
541
543
  [Ví dụ code](packages/example-react-router6/src/Form-for-test.tsx)
542
544
 
545
+ ### Hook useUrlState cho React (không có router)
546
+
547
+ Dành cho ứng dụng React không có router — Vite, Create React App, hoặc một widget được gắn vào trang mà bạn không kiểm soát. Hook ghi URL bằng `window.history` và đọc lại khi người dùng đi lui/tới cũng như khi có bất kỳ `pushState`/`replaceState` nào khác. Mọi component dùng chung một object state đều chia sẻ state đó, không cần bọc gì và không cần truyền qua props.
548
+
549
+ [Tài liệu API](packages/urlstate/react/useUrlState)
550
+
551
+ #### Ví dụ
552
+
553
+ ```typescript
554
+ // useFilters.ts
555
+ import { useUrlState } from 'state-in-url/react';
556
+
557
+ type FiltersState = { sort: 'name' | 'date'; page: number };
558
+ const FILTERS_STATE: FiltersState = { sort: 'name', page: 1 };
559
+
560
+ export function useFilters() {
561
+ return useUrlState(FILTERS_STATE);
562
+ }
563
+ ```
564
+
565
+ ```typescript
566
+ // FiltersBar.tsx
567
+ import { useFilters } from './useFilters';
568
+
569
+ export function FiltersBar() {
570
+ const { urlState, setUrl } = useFilters();
571
+
572
+ return <button onClick={() => setUrl({ page: urlState.page + 1 })}>Page {urlState.page}</button>;
573
+ }
574
+ ```
575
+
576
+ `state-in-url/astro` chính là hook này; nó được nói riêng ở phần dưới vì islands còn nhận `searchParams` dưới dạng prop.
577
+
543
578
  ### Hook useUrlState cho Astro
544
579
 
545
580
  Dành cho React island. Astro mặc định không có router phía client, nên hook ghi URL bằng `window.history` và đọc lại nó khi back/forward cũng như khi có bất kỳ lời gọi `pushState`/`replaceState` nào khác, kể cả `<ClientRouter />` của chính Astro. Các island trên cùng một trang chia sẻ state mà không cần bọc chúng trong bất cứ thứ gì.
@@ -818,9 +853,39 @@ const tags = [
818
853
 
819
854
  ## Các hook và helper khác
820
855
 
821
- ### Hook `useUrlStateBase` cho các router khác
856
+ ### Hook `useUrlStateBase` cho router khác
857
+
858
+ Dùng nó để tự dựng `useUrlState` cho một router mà gói này không cung cấp điểm vào, ví dụ TanStack Router. Hãy truyền một object đã được ghi nhớ (memoized) có `push(url)` và `replace(url)`.
859
+
860
+ Nếu **hoàn toàn không có router**, bạn không cần tới nó — hãy dùng [`state-in-url/react`](#hook-useurlstate-cho-react-không-có-router), vốn chính là hook này đã nối sẵn với `window.history`.
861
+
862
+ ```typescript
863
+ import { useUrlStateBase } from 'state-in-url/useUrlStateBase';
864
+
865
+ type FiltersState = { sort: 'name' | 'date'; page: number };
866
+ const FILTERS_STATE: FiltersState = { sort: 'name', page: 1 };
867
+
868
+ export function useFilters() {
869
+ const router = useYourRouter();
870
+ const nav = React.useMemo(
871
+ () => ({
872
+ push: (url: string) => router.navigate(url),
873
+ replace: (url: string) => router.navigate(url, { replace: true }),
874
+ }),
875
+ [router],
876
+ );
877
+
878
+ return useUrlStateBase(FILTERS_STATE, nav, ({ parse }) => parse(window.location.search));
879
+ }
880
+ ```
881
+
882
+ Hai điểm khác so với `useUrlState`:
883
+
884
+ - Nó trả về `state`, `updateUrl`, `updateState` chứ không phải `urlState`, `setUrl`, `setState`; và `updateUrl` mặc định là push, còn `setUrl` mặc định là replace.
885
+ - Tham số thứ ba nạp state ban đầu. Bỏ nó đi thì hook sẽ bỏ qua chuỗi truy vấn lúc trang được tải, nên một liên kết được chia sẻ sẽ mở ra với giá trị mặc định.
822
886
 
823
- Hook để tạo các hook `useUrlState` của riêng bạn với các router khác, ví dụ react-router hoặc tanstack router.
887
+ > [!NOTE]
888
+ > `useUrlState` không còn được export từ gốc của gói: mỗi `useUrlState` đều gắn với một framework, nên mỗi cái nằm ở điểm vào riêng (`state-in-url/next`, `/react`, `/react-router`, `/react-router6`, `/remix`, `/astro`). Điểm vào gốc chỉ giữ những gì không phụ thuộc framework: `useSharedState`, `useUrlEncode`, `useUrlStateBase`, `useLinkProps`, `encode`/`decode` và `encodeState`/`decodeState`.
824
889
 
825
890
  [Tài liệu API](packages/urlstate/useUrlStateBase)
826
891
 
package/README.zh-CN.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <!-- i18n:start -->
2
2
  [English](./README.md) · 简体中文 · [日本語](./README.ja.md) · [한국어](./README.ko.md) · [Русский](./README.ru.md) · [Español](./README.es.md) · [Português (BR)](./README.pt-BR.md) · [Français](./README.fr.md) · [Tiếng Việt](./README.vi.md)
3
- <!-- i18n:meta locale=zh-CN source=README.md source-blob=4d98ba970e6aab8f29987fbcba29fb6c61c2fc67 status=translated -->
3
+ <!-- i18n:meta locale=zh-CN source=README.md source-blob=ad78fdbbee096115a953813ed9a462e3393c5736 status=translated -->
4
4
  <!-- i18n:end -->
5
5
 
6
6
  <div align="center">
@@ -150,14 +150,16 @@ nuqs 也是一个不错的库——如果你希望每个值都是一条可读的
150
150
  - [示例](#示例)
151
151
  - [用于 React-Router 的 useUrlState hook](#用于-react-router-的-useurlstate-hook)
152
152
  - [示例](#示例-1)
153
- - [用于 Astro 的 useUrlState hook](#用于-astro-的-useurlstate-hook)
153
+ - [用于 React 的 useUrlState hook(无路由器)](#用于-react-的-useurlstate-hook无路由器)
154
154
  - [示例](#示例-2)
155
+ - [用于 Astro 的 useUrlState hook](#用于-astro-的-useurlstate-hook)
156
+ - [示例](#示例-3)
155
157
  - [实用技巧](#实用技巧)
156
158
  - [自定义 hook 以便捷地处理状态切片](#自定义-hook-以便捷地处理状态切片)
157
159
  - [使用复杂状态形状](#使用复杂状态形状)
158
160
  - [仅更新状态并手动同步到 URL](#仅更新状态并手动同步到-url)
159
161
  - [其他 hooks 和辅助函数](#其他-hooks-和辅助函数)
160
- - [`useUrlStateBase` hook 用于其他路由器](#useurlstatebase-hook-用于其他路由器)
162
+ - [用于其他路由器的 `useUrlStateBase` hook](#用于其他路由器的-useurlstatebase-hook)
161
163
  - [`useSharedState` hook 用于 React.js](#usesharedstate-hook-用于-reactjs)
162
164
  - [`useLinkProps` hook 用于 React.js](#uselinkprops-hook-用于-reactjs)
163
165
  - [`useUrlEncode` hook 用于 React.js](#useurlencode-hook-用于-reactjs)
@@ -540,6 +542,39 @@ const tags = [
540
542
 
541
543
  [示例代码](packages/example-react-router6/src/Form-for-test.tsx)
542
544
 
545
+ ### 用于 React 的 useUrlState hook(无路由器)
546
+
547
+ 适用于没有路由器的 React 应用 —— Vite、Create React App,或挂载到你无法控制的页面中的小部件。该 hook 通过 `window.history` 写入 URL,并在前进/后退以及任何其他 `pushState`/`replaceState` 时读回。使用同一个 state 对象的每个组件都共享该状态,既不需要包裹组件,也不需要通过 props 传递。
548
+
549
+ [API 文档](packages/urlstate/react/useUrlState)
550
+
551
+ #### 示例
552
+
553
+ ```typescript
554
+ // useFilters.ts
555
+ import { useUrlState } from 'state-in-url/react';
556
+
557
+ type FiltersState = { sort: 'name' | 'date'; page: number };
558
+ const FILTERS_STATE: FiltersState = { sort: 'name', page: 1 };
559
+
560
+ export function useFilters() {
561
+ return useUrlState(FILTERS_STATE);
562
+ }
563
+ ```
564
+
565
+ ```typescript
566
+ // FiltersBar.tsx
567
+ import { useFilters } from './useFilters';
568
+
569
+ export function FiltersBar() {
570
+ const { urlState, setUrl } = useFilters();
571
+
572
+ return <button onClick={() => setUrl({ page: urlState.page + 1 })}>Page {urlState.page}</button>;
573
+ }
574
+ ```
575
+
576
+ `state-in-url/astro` 就是这个 hook;之所以在下文单独说明,是因为 islands 还会把 `searchParams` 作为 prop 传入。
577
+
543
578
  ### 用于 Astro 的 useUrlState hook
544
579
 
545
580
  适用于 React 岛屿(islands)。Astro 默认没有客户端路由器,因此该 hook 通过 `window.history` 写入 URL,并在浏览器前进/后退以及任何其他 `pushState`/`replaceState` 调用时将其读回,Astro 自带的 `<ClientRouter />` 也包括在内。同一页面上的岛屿共享状态,无需用任何东西将它们包裹起来。
@@ -818,9 +853,39 @@ const tags = [
818
853
 
819
854
  ## 其他 hooks 和辅助函数
820
855
 
821
- ### `useUrlStateBase` hook 用于其他路由器
856
+ ### 用于其他路由器的 `useUrlStateBase` hook
857
+
858
+ 用它为本包未提供入口的路由器(例如 TanStack Router)构建自己的 `useUrlState`。传入任意带有 `push(url)` 和 `replace(url)` 的记忆化对象即可。
859
+
860
+ 如果**完全没有路由器**,则不需要它 —— 请使用 [`state-in-url/react`](#用于-react-的-useurlstate-hook无路由器),它就是已经接好 `window.history` 的这个 hook。
861
+
862
+ ```typescript
863
+ import { useUrlStateBase } from 'state-in-url/useUrlStateBase';
864
+
865
+ type FiltersState = { sort: 'name' | 'date'; page: number };
866
+ const FILTERS_STATE: FiltersState = { sort: 'name', page: 1 };
867
+
868
+ export function useFilters() {
869
+ const router = useYourRouter();
870
+ const nav = React.useMemo(
871
+ () => ({
872
+ push: (url: string) => router.navigate(url),
873
+ replace: (url: string) => router.navigate(url, { replace: true }),
874
+ }),
875
+ [router],
876
+ );
877
+
878
+ return useUrlStateBase(FILTERS_STATE, nav, ({ parse }) => parse(window.location.search));
879
+ }
880
+ ```
881
+
882
+ 与 `useUrlState` 有两点不同:
883
+
884
+ - 它返回 `state`、`updateUrl`、`updateState`,而不是 `urlState`、`setUrl`、`setState`;并且 `updateUrl` 默认为 push,而 `setUrl` 默认为 replace。
885
+ - 第三个参数用于填充初始 state。省略它,hook 就会忽略页面加载时 URL 中的查询字符串,于是分享出去的链接打开后只显示默认值。
822
886
 
823
- 用于使用其他路由器(例如 react-router 或 tanstack router)创建自己的 `useUrlState` hooks 的 Hooks。
887
+ > [!NOTE]
888
+ > `useUrlState` 不再从包的根入口导出 —— 每个 `useUrlState` 都与某个框架绑定,因此各自位于独立入口(`state-in-url/next`、`/react`、`/react-router`、`/react-router6`、`/remix`、`/astro`)。根入口只保留与框架无关的部分:`useSharedState`、`useUrlEncode`、`useUrlStateBase`、`useLinkProps`、`encode`/`decode` 和 `encodeState`/`decodeState`。
824
889
 
825
890
  [API 文档](packages/urlstate/useUrlStateBase)
826
891
 
@@ -1 +1 @@
1
- "use strict";var e=require("./useUrlState/useUrlState.js");exports.useUrlState=e.useUrlState;
1
+ "use strict";var e=require("../react/useUrlState/useUrlState.js");exports.useUrlState=e.useUrlState;
@@ -1 +1 @@
1
- import{useUrlState as t}from"./useUrlState/useUrlState.mjs";export{t as useUrlState};
1
+ import{useUrlState as t}from"../react/useUrlState/useUrlState.mjs";export{t as useUrlState};
@@ -1 +1 @@
1
- "use strict";var e=require("./useUrlState.js");exports.useUrlState=e.useUrlState;
1
+ "use strict";var e=require("../../react/useUrlState/useUrlState.js");exports.useUrlState=e.useUrlState;
@@ -1 +1 @@
1
- import{useUrlState as t}from"./useUrlState.mjs";export{t as useUrlState};
1
+ import{useUrlState as t}from"../../react/useUrlState/useUrlState.mjs";export{t as useUrlState};
@@ -1,85 +1 @@
1
- import { type JSONCompatible } from '../../utils';
2
- /**
3
- * Astro hook, for React islands. Returns `urlState`, `setState`, and `setUrl` functions
4
- *
5
- * Astro has no client-side router by default, so the URL is written with
6
- * `window.history` and read back on back/forward, on this hook's own writes,
7
- * and on any other `pushState`/`replaceState`, `<ClientRouter />` included.
8
- * Islands on one page share the state.
9
- *
10
- * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
11
- * @param {Object} params - Object with other parameters
12
- * @param {boolean} params.replace replace URL or push, default `true`
13
- * @param {?object} params.searchParams `Object.fromEntries(Astro.url.searchParams)`, passed as an island prop so the server render matches the URL
14
- * @returns {Object} [result] State and callbacks
15
- * @returns {Object} [result.urlState] - current state object
16
- * @returns {Function} [result.setUrl] - function to update state and url
17
- * @returns {Function} [result.setState] - function to update state only
18
- * @returns {Function} [result.reset] - function to reset state and url to default
19
- *
20
- * * Example:
21
- * ```ts
22
- * export const form = { name: '', age: 0 };
23
- * // island: <Form client:load searchParams={Object.fromEntries(Astro.url.searchParams)} />
24
- * const { urlState, setState, setUrl, reset } = useUrlState(form, { searchParams });
25
- *
26
- * setState({ name: 'test' });
27
- * setUrl({ name: 'test' }, { replace: false });
28
- * setUrl(curr => ({ ...curr, name: 'test' }));
29
- * // RESET state and url
30
- * setUrl((_curr, initialState) => initialState);
31
- * // Or
32
- * reset();
33
- * reset({ replace: false });
34
- * ```
35
- *
36
- * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/astro/useUrlState}
37
- */
38
- export declare function useUrlState<T extends JSONCompatible>(defaultState: T, params?: Params): {
39
- /**
40
- * State object. Don't mutate directly, use `setState` or `setUrl`
41
- */
42
- urlState: T;
43
- /**
44
- * * Example:
45
- * ```ts
46
- * setState({ name: 'test' });
47
- * // or
48
- * setState(curr => ({ ...curr, name: 'test' }) );
49
- * ```
50
- *
51
- * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/astro/useUrlState#setstate}
52
- */
53
- setState: (value: Partial<T> | ((currState: T, initial: T) => T)) => void;
54
- /**
55
- * * Example:
56
- * ```ts
57
- * setUrl({ name: 'test' });
58
- * // or
59
- * setUrl((curr) => ({ ...curr, name: 'test' }), { replace: false } );
60
- * * Reset
61
- * setUrl((_curr, initialState) => initialState, { replace: false } );
62
- * ```
63
- *
64
- * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/astro/useUrlState#seturl}
65
- */
66
- setUrl: (value?: Partial<T> | ((currState: T, initialState: T) => T), options?: Options) => void;
67
- /**
68
- * * Example:
69
- * ```ts
70
- * reset();
71
- * // or
72
- * reset({ replace: false })
73
- * ```
74
- *
75
- * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/astro/useUrlState#reset}
76
- */
77
- reset: (options?: Options) => void;
78
- };
79
- export type Options = {
80
- replace?: boolean;
81
- };
82
- export type Params = {
83
- searchParams?: object;
84
- replace?: boolean;
85
- };
1
+ export * from '../../react/useUrlState';
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import { decode, encode } from './encoder';
2
2
  import { decodeState, encodeState } from './encodeState';
3
- import { useUrlState } from './next';
4
3
  import { useLinkProps } from './useLinkProps';
5
4
  import { useSharedState } from './useSharedState';
6
5
  import { useUrlEncode } from './useUrlEncode';
7
6
  import { useUrlStateBase } from './useUrlStateBase';
8
7
  import { isSSR, type JSONCompatible, type Type, typeOf } from './utils';
9
- export { decode, decodeState, encode, encodeState, isSSR, typeOf, useLinkProps, useSharedState, useUrlEncode, useUrlState, useUrlStateBase, };
8
+ export { decode, decodeState, encode, encodeState, isSSR, typeOf, useLinkProps, useSharedState, useUrlEncode, useUrlStateBase, };
10
9
  export type { JSONCompatible, Type };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("./encoder/encoder.js");var r=require("./encodeState/encodeState.js");var a=require("./next/useUrlState/useUrlState.js");var s=require("./useLinkProps/useLinkProps.js");var u=require("./useSharedState/useSharedState.js");var S=require("./useUrlEncode/useUrlEncode.js");var d=require("./useUrlStateBase/useUrlStateBase.js");var t=require("./utils.js");exports.decode=e.decode;exports.encode=e.encode;exports.decodeState=r.decodeState;exports.encodeState=r.encodeState;exports.useUrlState=a.useUrlState;exports.useLinkProps=s.useLinkProps;exports.useSharedState=u.useSharedState;exports.useUrlEncode=S.useUrlEncode;exports.useUrlStateBase=d.useUrlStateBase;exports.isSSR=t.isSSR;exports.typeOf=t.typeOf;
1
+ "use strict";var e=require("./encoder/encoder.js");var r=require("./encodeState/encodeState.js");var a=require("./useLinkProps/useLinkProps.js");var s=require("./useSharedState/useSharedState.js");var u=require("./useUrlEncode/useUrlEncode.js");var d=require("./useUrlStateBase/useUrlStateBase.js");var t=require("./utils.js");exports.decode=e.decode;exports.encode=e.encode;exports.decodeState=r.decodeState;exports.encodeState=r.encodeState;exports.useLinkProps=a.useLinkProps;exports.useSharedState=s.useSharedState;exports.useUrlEncode=u.useUrlEncode;exports.useUrlStateBase=d.useUrlStateBase;exports.isSSR=t.isSSR;exports.typeOf=t.typeOf;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{decode as r,encode as t}from"./encoder/encoder.mjs";import{decodeState as f,encodeState as d}from"./encodeState/encodeState.mjs";import{useUrlState as s}from"./next/useUrlState/useUrlState.mjs";import{useLinkProps as S}from"./useLinkProps/useLinkProps.mjs";import{useSharedState as c}from"./useSharedState/useSharedState.mjs";import{useUrlEncode as n}from"./useUrlEncode/useUrlEncode.mjs";import{useUrlStateBase as U}from"./useUrlStateBase/useUrlStateBase.mjs";import{isSSR as h,typeOf as k}from"./utils.mjs";export{r as decode,f as decodeState,t as encode,d as encodeState,h as isSSR,k as typeOf,S as useLinkProps,c as useSharedState,n as useUrlEncode,s as useUrlState,U as useUrlStateBase};
1
+ import{decode as r,encode as t}from"./encoder/encoder.mjs";import{decodeState as d,encodeState as f}from"./encodeState/encodeState.mjs";import{useLinkProps as s}from"./useLinkProps/useLinkProps.mjs";import{useSharedState as S}from"./useSharedState/useSharedState.mjs";import{useUrlEncode as c}from"./useUrlEncode/useUrlEncode.mjs";import{useUrlStateBase as u}from"./useUrlStateBase/useUrlStateBase.mjs";import{isSSR as l,typeOf as U}from"./utils.mjs";export{r as decode,d as decodeState,t as encode,f as encodeState,l as isSSR,U as typeOf,s as useLinkProps,S as useSharedState,c as useUrlEncode,u as useUrlStateBase};
@@ -1 +1 @@
1
- import r from"react";import{useRouter as y}from"next/navigation";import{parseSPObj as p}from"../../parseSPObj.mjs";import{useUrlStateBase as C}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as m,filterUnknownParams as f,getParams as v,subscribeToUrl as H,isSSR as M,filterUnknownParamsClient as j,getSearch as S}from"../../utils.mjs";function w(s,t){const c=r.useMemo(()=>t?.useHistory===void 0?true:!!t?.useHistory,[]);const a=y();const U=r.useMemo(()=>({push:(...e)=>{if(c){m.push(...e)}else{a.push(...e)}},replace:(...e)=>{if(c){m.replace(...e)}else{a.replace(...e)}}}),[a]);const{state:b,updateState:u,updateUrl:n,reset:i,getState:d,pendingUrlUpdate:h}=C(s,U,({parse:e})=>{return M?p(f(s,t?.searchParams),s):e(j(s,t?.searchParams||S()))});const l=r.useCallback(()=>{if(h())return;u(f(s,p(Object.fromEntries(v(S()).entries()),s)))},[]);r.useEffect(()=>{l();return H(l)},[l]);const o=r.useMemo(()=>{const e={...E};if(t?.scroll!==void 0)e.scroll=t.scroll;if(t?.replace!==void 0)e.replace=t.replace;return e},[t?.scroll,t?.replace]);const P=r.useCallback((e,k)=>n(e,{...o,...k}),[n,o]);const g=r.useCallback(e=>{i({...o,...e})},[i,o]);return{setState:u,setUrl:P,urlState:b,reset:g,getState:d}}const E={replace:true,scroll:false};export{w as useUrlState};
1
+ import r from"react";import{useRouter as y}from"next/navigation";import{parseSPObj as p}from"../../parseSPObj.mjs";import{useUrlStateBase as C}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as m,isSSR as v,filterUnknownParams as f,filterUnknownParamsClient as H,getSearch as S,getParams as M,subscribeToUrl as j}from"../../utils.mjs";function w(s,t){const c=r.useMemo(()=>t?.useHistory===void 0?true:!!t?.useHistory,[]);const a=y();const U=r.useMemo(()=>({push:(...e)=>{if(c){m.push(...e)}else{a.push(...e)}},replace:(...e)=>{if(c){m.replace(...e)}else{a.replace(...e)}}}),[a]);const{state:b,updateState:u,updateUrl:n,reset:i,getState:d,pendingUrlUpdate:h}=C(s,U,({parse:e})=>{return v?p(f(s,t?.searchParams),s):e(H(s,t?.searchParams||S()))});const l=r.useCallback(()=>{if(h())return;u(f(s,p(Object.fromEntries(M(S()).entries()),s)))},[]);r.useEffect(()=>{l();return j(l)},[l]);const o=r.useMemo(()=>{const e={...E};if(t?.scroll!==void 0)e.scroll=t.scroll;if(t?.replace!==void 0)e.replace=t.replace;return e},[t?.scroll,t?.replace]);const P=r.useCallback((e,k)=>n(e,{...o,...k}),[n,o]);const g=r.useCallback(e=>{i({...o,...e})},[i,o]);return{setState:u,setUrl:P,urlState:b,reset:g,getState:d}}const E={replace:true,scroll:false};export{w as useUrlState};
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-in-url",
3
- "version": "6.5.0",
3
+ "version": "7.0.0",
4
4
  "description": "Store state in URL as in object, types and structure are preserved, with TS validation. Same API as React.useState, wthout any hasssle or boilerplate. Next.js@14-16, react-router@6-7, remix@2, and Astro.",
5
5
  "homepage": "https://state-in-url.dev",
6
6
  "repository": {
@@ -43,6 +43,11 @@
43
43
  "types": "./dist/next/index.d.ts",
44
44
  "default": "./dist/next/index.mjs"
45
45
  },
46
+ "./react": {
47
+ "require": "./dist/react/index.js",
48
+ "types": "./dist/react/index.d.ts",
49
+ "default": "./dist/react/index.mjs"
50
+ },
46
51
  "./react-router6": {
47
52
  "require": "./dist/react-router6/index.js",
48
53
  "types": "./dist/react-router6/index.d.ts",
@@ -0,0 +1 @@
1
+ export { useUrlState } from './useUrlState';
@@ -0,0 +1 @@
1
+ "use strict";var e=require("./useUrlState/useUrlState.js");exports.useUrlState=e.useUrlState;
@@ -0,0 +1 @@
1
+ import{useUrlState as t}from"./useUrlState/useUrlState.mjs";export{t as useUrlState};
@@ -0,0 +1 @@
1
+ export * from './useUrlState';
@@ -0,0 +1 @@
1
+ "use strict";var e=require("./useUrlState.js");exports.useUrlState=e.useUrlState;
@@ -0,0 +1 @@
1
+ import{useUrlState as t}from"./useUrlState.mjs";export{t as useUrlState};
@@ -0,0 +1,86 @@
1
+ import { type JSONCompatible } from '../../utils';
2
+ /**
3
+ * Hook for React with no router — Vite, Create React App, an Astro island.
4
+ * Returns `urlState`, `setState`, and `setUrl` functions
5
+ *
6
+ * The URL is written with `window.history` and read back on back/forward, on
7
+ * this hook's own writes, and on any other `pushState`/`replaceState`, Astro's
8
+ * `<ClientRouter />` included. Every component sharing the default state object
9
+ * shares the state, islands on one page included.
10
+ *
11
+ * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
12
+ * @param {Object} params - Object with other parameters
13
+ * @param {boolean} params.replace replace URL or push, default `true`
14
+ * @param {?object} params.searchParams Query params from a server render, so it matches the URL. Astro islands pass `Object.fromEntries(Astro.url.searchParams)` as a prop; a client-only app needs nothing here
15
+ * @returns {Object} [result] State and callbacks
16
+ * @returns {Object} [result.urlState] - current state object
17
+ * @returns {Function} [result.setUrl] - function to update state and url
18
+ * @returns {Function} [result.setState] - function to update state only
19
+ * @returns {Function} [result.reset] - function to reset state and url to default
20
+ *
21
+ * * Example:
22
+ * ```ts
23
+ * export const form = { name: '', age: 0 };
24
+ * // Astro island: <Form client:load searchParams={Object.fromEntries(Astro.url.searchParams)} />
25
+ * const { urlState, setState, setUrl, reset } = useUrlState(form, { searchParams });
26
+ *
27
+ * setState({ name: 'test' });
28
+ * setUrl({ name: 'test' }, { replace: false });
29
+ * setUrl(curr => ({ ...curr, name: 'test' }));
30
+ * // RESET state and url
31
+ * setUrl((_curr, initialState) => initialState);
32
+ * // Or
33
+ * reset();
34
+ * reset({ replace: false });
35
+ * ```
36
+ *
37
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react/useUrlState}
38
+ */
39
+ export declare function useUrlState<T extends JSONCompatible>(defaultState: T, params?: Params): {
40
+ /**
41
+ * State object. Don't mutate directly, use `setState` or `setUrl`
42
+ */
43
+ urlState: T;
44
+ /**
45
+ * * Example:
46
+ * ```ts
47
+ * setState({ name: 'test' });
48
+ * // or
49
+ * setState(curr => ({ ...curr, name: 'test' }) );
50
+ * ```
51
+ *
52
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react/useUrlState#setstate}
53
+ */
54
+ setState: (value: Partial<T> | ((currState: T, initial: T) => T)) => void;
55
+ /**
56
+ * * Example:
57
+ * ```ts
58
+ * setUrl({ name: 'test' });
59
+ * // or
60
+ * setUrl((curr) => ({ ...curr, name: 'test' }), { replace: false } );
61
+ * * Reset
62
+ * setUrl((_curr, initialState) => initialState, { replace: false } );
63
+ * ```
64
+ *
65
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react/useUrlState#seturl}
66
+ */
67
+ setUrl: (value?: Partial<T> | ((currState: T, initialState: T) => T), options?: Options) => void;
68
+ /**
69
+ * * Example:
70
+ * ```ts
71
+ * reset();
72
+ * // or
73
+ * reset({ replace: false })
74
+ * ```
75
+ *
76
+ * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react/useUrlState#reset}
77
+ */
78
+ reset: (options?: Options) => void;
79
+ };
80
+ export type Options = {
81
+ replace?: boolean;
82
+ };
83
+ export type Params = {
84
+ searchParams?: object;
85
+ replace?: boolean;
86
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-in-url",
3
- "version": "6.5.0",
3
+ "version": "7.0.0",
4
4
  "description": "Store state in URL as in object, types and structure are preserved, with TS validation. Same API as React.useState, wthout any hasssle or boilerplate. Next.js@14-16, react-router@6-7, remix@2, and Astro.",
5
5
  "homepage": "https://state-in-url.dev",
6
6
  "repository": {
@@ -43,6 +43,11 @@
43
43
  "types": "./dist/next/index.d.ts",
44
44
  "default": "./dist/next/index.mjs"
45
45
  },
46
+ "./react": {
47
+ "require": "./dist/react/index.js",
48
+ "types": "./dist/react/index.d.ts",
49
+ "default": "./dist/react/index.mjs"
50
+ },
46
51
  "./react-router6": {
47
52
  "require": "./dist/react-router6/index.js",
48
53
  "types": "./dist/react-router6/index.d.ts",
@@ -18,7 +18,7 @@ sources:
18
18
  metadata:
19
19
  type: framework
20
20
  library: state-in-url
21
- library_version: '6.5.0'
21
+ library_version: '7.0.0'
22
22
  framework: astro
23
23
  ---
24
24
 
@@ -16,7 +16,7 @@ sources:
16
16
  metadata:
17
17
  type: core
18
18
  library: state-in-url
19
- library_version: '6.5.0'
19
+ library_version: '7.0.0'
20
20
  ---
21
21
 
22
22
  # state-in-url — Feature state hook
@@ -15,7 +15,7 @@ sources:
15
15
  metadata:
16
16
  type: composition
17
17
  library: state-in-url
18
- library_version: '6.5.0'
18
+ library_version: '7.0.0'
19
19
  ---
20
20
 
21
21
  This skill builds on `state-in-url/feature-state-hook`. Read it first for the module-scoped default-state rule.