@violetflux/kerros 0.1.0 → 0.1.2
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.de.md +89 -0
- package/README.es.md +89 -0
- package/README.fr.md +89 -0
- package/README.ja.md +90 -0
- package/README.ko.md +90 -0
- package/README.md +212 -18
- package/README.zh-CN.md +226 -0
- package/package.json +5 -2
package/README.de.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://violetflux.github.io/kerros/de/"><img src="https://raw.githubusercontent.com/violetflux/kerros/main/docs/public/banner.svg" alt="Kerros" width="100%" /></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.md">English</a> ·
|
|
7
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.zh-CN.md">简体中文</a> ·
|
|
8
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ja.md">日本語</a> ·
|
|
9
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ko.md">한국어</a> ·
|
|
10
|
+
Deutsch ·
|
|
11
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.fr.md">Français</a> ·
|
|
12
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.es.md">Español</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
Kerros lässt React-State dort, wo er natürlich hingehört: in Hooks und unter Providern. Die Bibliothek ergänzt gezielte Selector-Abonnements, ohne Reducer, Actions, Proxies oder globale Singletons vorzuschreiben.
|
|
16
|
+
|
|
17
|
+
- Ein Store ist ein normaler React Hook
|
|
18
|
+
- Selektoren geben ein Objekt mit den benötigten Werten zurück
|
|
19
|
+
- Oberste Felder werden flach mit `Object.is` verglichen
|
|
20
|
+
- Jeder Provider besitzt eine isolierte Store-Instanz
|
|
21
|
+
- Stores lassen sich über einseitige Abhängigkeiten komponieren
|
|
22
|
+
- Unterstützt React 17, 18 und 19
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
| Paketmanager | Befehl |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| npm | `npm install @violetflux/kerros` |
|
|
29
|
+
| pnpm | `pnpm add @violetflux/kerros` |
|
|
30
|
+
| Yarn | `yarn add @violetflux/kerros` |
|
|
31
|
+
| Bun | `bun add @violetflux/kerros` |
|
|
32
|
+
|
|
33
|
+
## Store erstellen
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { createStore } from '@violetflux/kerros'
|
|
37
|
+
import { useState } from 'react'
|
|
38
|
+
|
|
39
|
+
export const [useCounter, CounterProvider] = createStore(() => {
|
|
40
|
+
const [count, setCount] = useState(0)
|
|
41
|
+
return { count, setCount }
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Im Store Hook können weiterhin `useState`, `useReducer`, Context, SDK Hooks und eigene Hooks verwendet werden.
|
|
46
|
+
|
|
47
|
+
## Provider einbinden und Werte auswählen
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
function Counter() {
|
|
51
|
+
const { count, setCount } = useCounter(s => ({
|
|
52
|
+
count: s.count,
|
|
53
|
+
setCount: s.setCount,
|
|
54
|
+
}))
|
|
55
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function App() {
|
|
59
|
+
return <CounterProvider><Counter /></CounterProvider>
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Der Selektor darf inline stehen. Änderungen an nicht ausgewählten Feldern rendern `Counter` nicht neu.
|
|
64
|
+
|
|
65
|
+
## Funktionsweise
|
|
66
|
+
|
|
67
|
+
Der Provider führt den Store Hook aus und gibt über Context nur einen stabilen Abonnement-Container weiter. Bestätigte Snapshots werden mit `subscribe/getSnapshot` veröffentlicht. Eine Komponente rendert nur neu, wenn sich ihre Auswahl ändert.
|
|
68
|
+
|
|
69
|
+
Ohne verstecktes Modul-Singleton kann derselbe Provider mehrfach eingebunden, über Props initialisiert, in Tests isoliert oder auf einen Teilbaum begrenzt werden.
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
function createStore<TStore, TProps = Record<never, never>>(
|
|
75
|
+
useStoreValue: (props: TProps) => TStore,
|
|
76
|
+
): readonly [StoreHook<TStore>, StoreProvider<TProps>]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Der zurückgegebene Store Hook verlangt einen Selektor, der ein Objekt liefert. Außerhalb des passenden Providers wird ein verständlicher Fehler ausgelöst. Strict Mode und Server Rendering werden unterstützt.
|
|
80
|
+
|
|
81
|
+
## Dokumentation
|
|
82
|
+
|
|
83
|
+
- [Einführung](https://violetflux.github.io/kerros/de/guide/introduction)
|
|
84
|
+
- [Erste Schritte](https://violetflux.github.io/kerros/de/guide/getting-started)
|
|
85
|
+
- [Selektoren](https://violetflux.github.io/kerros/de/guide/selectors)
|
|
86
|
+
- [Store-Komposition](https://violetflux.github.io/kerros/de/guide/composition)
|
|
87
|
+
- [API](https://violetflux.github.io/kerros/de/api/)
|
|
88
|
+
|
|
89
|
+
[MIT License](https://github.com/violetflux/kerros/blob/main/LICENSE)
|
package/README.es.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://violetflux.github.io/kerros/es/"><img src="https://raw.githubusercontent.com/violetflux/kerros/main/docs/public/banner.svg" alt="Kerros" width="100%" /></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.md">English</a> ·
|
|
7
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.zh-CN.md">简体中文</a> ·
|
|
8
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ja.md">日本語</a> ·
|
|
9
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ko.md">한국어</a> ·
|
|
10
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.de.md">Deutsch</a> ·
|
|
11
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.fr.md">Français</a> ·
|
|
12
|
+
Español
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
Kerros mantiene el estado de React donde pertenece de forma natural: dentro de Hooks y bajo Providers. Añade suscripciones precisas mediante selectores sin imponer reducers, actions, proxies ni un singleton global.
|
|
16
|
+
|
|
17
|
+
- Un Store es un Hook de React normal
|
|
18
|
+
- Los selectores devuelven un objeto con los valores necesarios
|
|
19
|
+
- Los campos superiores se comparan superficialmente con `Object.is`
|
|
20
|
+
- Cada Provider posee una instancia de Store aislada
|
|
21
|
+
- Los Stores se componen mediante dependencias unidireccionales
|
|
22
|
+
- Compatible con React 17, 18 y 19
|
|
23
|
+
|
|
24
|
+
## Instalación
|
|
25
|
+
|
|
26
|
+
| Gestor | Comando |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| npm | `npm install @violetflux/kerros` |
|
|
29
|
+
| pnpm | `pnpm add @violetflux/kerros` |
|
|
30
|
+
| Yarn | `yarn add @violetflux/kerros` |
|
|
31
|
+
| Bun | `bun add @violetflux/kerros` |
|
|
32
|
+
|
|
33
|
+
## Crear un Store
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { createStore } from '@violetflux/kerros'
|
|
37
|
+
import { useState } from 'react'
|
|
38
|
+
|
|
39
|
+
export const [useCounter, CounterProvider] = createStore(() => {
|
|
40
|
+
const [count, setCount] = useState(0)
|
|
41
|
+
return { count, setCount }
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
El Hook del Store puede seguir usando `useState`, `useReducer`, Context, Hooks de SDK y Hooks personalizados.
|
|
46
|
+
|
|
47
|
+
## Montar el Provider y seleccionar valores
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
function Counter() {
|
|
51
|
+
const { count, setCount } = useCounter(s => ({
|
|
52
|
+
count: s.count,
|
|
53
|
+
setCount: s.setCount,
|
|
54
|
+
}))
|
|
55
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function App() {
|
|
59
|
+
return <CounterProvider><Counter /></CounterProvider>
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
El selector puede escribirse en línea. Cambiar un campo no seleccionado no vuelve a renderizar `Counter`.
|
|
64
|
+
|
|
65
|
+
## Cómo funciona
|
|
66
|
+
|
|
67
|
+
El Provider ejecuta el Hook del Store y Context solo transporta un contenedor de suscripción estable. Los snapshots confirmados se publican mediante `subscribe/getSnapshot`. Cada componente se renderiza de nuevo únicamente cuando cambia su selección.
|
|
68
|
+
|
|
69
|
+
Sin un singleton de módulo oculto, el mismo Provider puede montarse varias veces, inicializarse con props, aislarse en pruebas o limitarse a un subárbol.
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
function createStore<TStore, TProps = Record<never, never>>(
|
|
75
|
+
useStoreValue: (props: TProps) => TStore,
|
|
76
|
+
): readonly [StoreHook<TStore>, StoreProvider<TProps>]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
El Hook devuelto requiere un selector que retorne un objeto. Usarlo fuera de su Provider correspondiente produce un error claro. Admite Strict Mode y renderizado en servidor.
|
|
80
|
+
|
|
81
|
+
## Documentación
|
|
82
|
+
|
|
83
|
+
- [Introducción](https://violetflux.github.io/kerros/es/guide/introduction)
|
|
84
|
+
- [Primeros pasos](https://violetflux.github.io/kerros/es/guide/getting-started)
|
|
85
|
+
- [Selectores](https://violetflux.github.io/kerros/es/guide/selectors)
|
|
86
|
+
- [Composición de Stores](https://violetflux.github.io/kerros/es/guide/composition)
|
|
87
|
+
- [API](https://violetflux.github.io/kerros/es/api/)
|
|
88
|
+
|
|
89
|
+
[Licencia MIT](https://github.com/violetflux/kerros/blob/main/LICENSE)
|
package/README.fr.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://violetflux.github.io/kerros/fr/"><img src="https://raw.githubusercontent.com/violetflux/kerros/main/docs/public/banner.svg" alt="Kerros" width="100%" /></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.md">English</a> ·
|
|
7
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.zh-CN.md">简体中文</a> ·
|
|
8
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ja.md">日本語</a> ·
|
|
9
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ko.md">한국어</a> ·
|
|
10
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.de.md">Deutsch</a> ·
|
|
11
|
+
Français ·
|
|
12
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.es.md">Español</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
Kerros conserve l'état React là où il se trouve naturellement : dans les Hooks et sous les Providers. Il ajoute des abonnements précis par sélecteur sans imposer reducers, actions, proxies ou singleton global.
|
|
16
|
+
|
|
17
|
+
- Un Store est un Hook React ordinaire
|
|
18
|
+
- Les sélecteurs renvoient un objet contenant les valeurs nécessaires
|
|
19
|
+
- Les champs de premier niveau sont comparés avec `Object.is`
|
|
20
|
+
- Chaque Provider possède une instance de Store isolée
|
|
21
|
+
- Les Stores se composent grâce à des dépendances unidirectionnelles
|
|
22
|
+
- Compatible avec React 17, 18 et 19
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
| Gestionnaire | Commande |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| npm | `npm install @violetflux/kerros` |
|
|
29
|
+
| pnpm | `pnpm add @violetflux/kerros` |
|
|
30
|
+
| Yarn | `yarn add @violetflux/kerros` |
|
|
31
|
+
| Bun | `bun add @violetflux/kerros` |
|
|
32
|
+
|
|
33
|
+
## Créer un Store
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { createStore } from '@violetflux/kerros'
|
|
37
|
+
import { useState } from 'react'
|
|
38
|
+
|
|
39
|
+
export const [useCounter, CounterProvider] = createStore(() => {
|
|
40
|
+
const [count, setCount] = useState(0)
|
|
41
|
+
return { count, setCount }
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Le Hook du Store peut continuer à utiliser `useState`, `useReducer`, Context, des Hooks SDK et vos Hooks personnalisés.
|
|
46
|
+
|
|
47
|
+
## Monter le Provider et sélectionner les valeurs
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
function Counter() {
|
|
51
|
+
const { count, setCount } = useCounter(s => ({
|
|
52
|
+
count: s.count,
|
|
53
|
+
setCount: s.setCount,
|
|
54
|
+
}))
|
|
55
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function App() {
|
|
59
|
+
return <CounterProvider><Counter /></CounterProvider>
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Le sélecteur peut rester en ligne. La modification d'un champ non sélectionné ne provoque pas un nouveau rendu de `Counter`.
|
|
64
|
+
|
|
65
|
+
## Fonctionnement
|
|
66
|
+
|
|
67
|
+
Le Provider exécute le Hook du Store et Context ne transporte qu'un conteneur d'abonnement stable. Les snapshots validés sont publiés via `subscribe/getSnapshot`. Un composant n'est rendu à nouveau que lorsque sa sélection change.
|
|
68
|
+
|
|
69
|
+
Sans singleton de module caché, un même Provider peut être monté plusieurs fois, initialisé par des props, isolé dans les tests ou limité à un sous-arbre.
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
function createStore<TStore, TProps = Record<never, never>>(
|
|
75
|
+
useStoreValue: (props: TProps) => TStore,
|
|
76
|
+
): readonly [StoreHook<TStore>, StoreProvider<TProps>]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Le Hook retourné exige un sélecteur qui renvoie un objet. Son utilisation hors du Provider correspondant lève une erreur claire. Strict Mode et le rendu serveur sont pris en charge.
|
|
80
|
+
|
|
81
|
+
## Documentation
|
|
82
|
+
|
|
83
|
+
- [Introduction](https://violetflux.github.io/kerros/fr/guide/introduction)
|
|
84
|
+
- [Bien démarrer](https://violetflux.github.io/kerros/fr/guide/getting-started)
|
|
85
|
+
- [Sélecteurs](https://violetflux.github.io/kerros/fr/guide/selectors)
|
|
86
|
+
- [Composition des Stores](https://violetflux.github.io/kerros/fr/guide/composition)
|
|
87
|
+
- [API](https://violetflux.github.io/kerros/fr/api/)
|
|
88
|
+
|
|
89
|
+
[Licence MIT](https://github.com/violetflux/kerros/blob/main/LICENSE)
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://violetflux.github.io/kerros/ja/"><img src="https://raw.githubusercontent.com/violetflux/kerros/main/docs/public/banner.svg" alt="Kerros" width="100%" /></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.md">English</a> ·
|
|
7
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.zh-CN.md">简体中文</a> ·
|
|
8
|
+
日本語 ·
|
|
9
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ko.md">한국어</a> ·
|
|
10
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.de.md">Deutsch</a> ·
|
|
11
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.fr.md">Français</a> ·
|
|
12
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.es.md">Español</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
Kerros は React の状態を Hook の中、Provider の下という自然な場所に保ちます。reducer、action、proxy、グローバル singleton を導入せず、selector による限定的な購読を追加します。
|
|
16
|
+
|
|
17
|
+
- Store は通常の React Hook
|
|
18
|
+
- selector は必要な値だけをオブジェクトで返す
|
|
19
|
+
- 選択したトップレベルフィールドを `Object.is` で浅く比較
|
|
20
|
+
- Provider ごとに独立した Store インスタンス
|
|
21
|
+
- 一方向の依存関係で Store を合成可能
|
|
22
|
+
- React 17、18、19 をサポート
|
|
23
|
+
|
|
24
|
+
## インストール
|
|
25
|
+
|
|
26
|
+
| パッケージマネージャー | コマンド |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| npm | `npm install @violetflux/kerros` |
|
|
29
|
+
| pnpm | `pnpm add @violetflux/kerros` |
|
|
30
|
+
| Yarn | `yarn add @violetflux/kerros` |
|
|
31
|
+
| Bun | `bun add @violetflux/kerros` |
|
|
32
|
+
|
|
33
|
+
## Store を作成する
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { createStore } from '@violetflux/kerros'
|
|
37
|
+
import { useState } from 'react'
|
|
38
|
+
|
|
39
|
+
export const [useCounter, CounterProvider] = createStore(() => {
|
|
40
|
+
const [count, setCount] = useState(0)
|
|
41
|
+
return { count, setCount }
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Store Hook 内では `useState`、`useReducer`、Context、SDK Hook、カスタム Hook をそのまま利用できます。
|
|
46
|
+
|
|
47
|
+
## Provider を配置して値を選択する
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
function Counter() {
|
|
51
|
+
const { count, setCount } = useCounter(s => ({
|
|
52
|
+
count: s.count,
|
|
53
|
+
setCount: s.setCount,
|
|
54
|
+
}))
|
|
55
|
+
|
|
56
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function App() {
|
|
60
|
+
return <CounterProvider><Counter /></CounterProvider>
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
selector はインラインで記述できます。選択していないフィールドの変更では `Counter` は再レンダーされません。
|
|
65
|
+
|
|
66
|
+
## 仕組み
|
|
67
|
+
|
|
68
|
+
Provider は Store Hook を実行し、Context には安定した購読コンテナだけを渡します。コミット済みスナップショットは `subscribe/getSnapshot` で公開され、各コンポーネントは選択結果が変わったときだけ再レンダーされます。
|
|
69
|
+
|
|
70
|
+
モジュール内の隠れた singleton がないため、Provider の複数配置、props による初期化、テストでの分離、サブツリー単位のスコープが可能です。
|
|
71
|
+
|
|
72
|
+
## API
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
function createStore<TStore, TProps = Record<never, never>>(
|
|
76
|
+
useStoreValue: (props: TProps) => TStore,
|
|
77
|
+
): readonly [StoreHook<TStore>, StoreProvider<TProps>]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
返される Store Hook にはオブジェクトを返す selector が必須です。対応する Provider の外で呼び出すと明確なエラーを送出します。Strict Mode とサーバーレンダリングをサポートします。
|
|
81
|
+
|
|
82
|
+
## ドキュメント
|
|
83
|
+
|
|
84
|
+
- [はじめに](https://violetflux.github.io/kerros/ja/guide/introduction)
|
|
85
|
+
- [インストールと基本](https://violetflux.github.io/kerros/ja/guide/getting-started)
|
|
86
|
+
- [Selector](https://violetflux.github.io/kerros/ja/guide/selectors)
|
|
87
|
+
- [Store の合成](https://violetflux.github.io/kerros/ja/guide/composition)
|
|
88
|
+
- [API](https://violetflux.github.io/kerros/ja/api/)
|
|
89
|
+
|
|
90
|
+
[MIT License](https://github.com/violetflux/kerros/blob/main/LICENSE)
|
package/README.ko.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://violetflux.github.io/kerros/ko/"><img src="https://raw.githubusercontent.com/violetflux/kerros/main/docs/public/banner.svg" alt="Kerros" width="100%" /></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.md">English</a> ·
|
|
7
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.zh-CN.md">简体中文</a> ·
|
|
8
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ja.md">日本語</a> ·
|
|
9
|
+
한국어 ·
|
|
10
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.de.md">Deutsch</a> ·
|
|
11
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.fr.md">Français</a> ·
|
|
12
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.es.md">Español</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
Kerros는 React 상태를 Hook 안과 Provider 아래라는 자연스러운 위치에 둡니다. reducer, action, proxy, 전역 singleton을 도입하지 않고 selector 기반의 정밀 구독을 제공합니다.
|
|
16
|
+
|
|
17
|
+
- Store는 평범한 React Hook
|
|
18
|
+
- selector는 필요한 값만 객체로 반환
|
|
19
|
+
- 선택 객체의 최상위 필드를 `Object.is`로 얕게 비교
|
|
20
|
+
- Provider마다 격리된 Store 인스턴스
|
|
21
|
+
- 단방향 의존성을 통한 Store 조합
|
|
22
|
+
- React 17, 18, 19 지원
|
|
23
|
+
|
|
24
|
+
## 설치
|
|
25
|
+
|
|
26
|
+
| 패키지 매니저 | 명령 |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| npm | `npm install @violetflux/kerros` |
|
|
29
|
+
| pnpm | `pnpm add @violetflux/kerros` |
|
|
30
|
+
| Yarn | `yarn add @violetflux/kerros` |
|
|
31
|
+
| Bun | `bun add @violetflux/kerros` |
|
|
32
|
+
|
|
33
|
+
## Store 만들기
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { createStore } from '@violetflux/kerros'
|
|
37
|
+
import { useState } from 'react'
|
|
38
|
+
|
|
39
|
+
export const [useCounter, CounterProvider] = createStore(() => {
|
|
40
|
+
const [count, setCount] = useState(0)
|
|
41
|
+
return { count, setCount }
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Store Hook 안에서 `useState`, `useReducer`, Context, SDK Hook, 사용자 Hook을 그대로 사용할 수 있습니다.
|
|
46
|
+
|
|
47
|
+
## Provider를 마운트하고 필요한 값 선택하기
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
function Counter() {
|
|
51
|
+
const { count, setCount } = useCounter(s => ({
|
|
52
|
+
count: s.count,
|
|
53
|
+
setCount: s.setCount,
|
|
54
|
+
}))
|
|
55
|
+
|
|
56
|
+
return <button onClick={() => setCount(count + 1)}>{count}</button>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function App() {
|
|
60
|
+
return <CounterProvider><Counter /></CounterProvider>
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
selector는 인라인으로 작성할 수 있습니다. 선택하지 않은 필드가 바뀌어도 `Counter`는 다시 렌더링되지 않습니다.
|
|
65
|
+
|
|
66
|
+
## 작동 방식
|
|
67
|
+
|
|
68
|
+
Provider는 Store Hook을 실행하고 Context에는 안정적인 구독 컨테이너만 전달합니다. 커밋된 스냅샷은 `subscribe/getSnapshot`으로 게시되며 컴포넌트는 선택 결과가 바뀔 때만 다시 렌더링됩니다.
|
|
69
|
+
|
|
70
|
+
숨겨진 모듈 singleton이 없으므로 같은 Provider를 여러 번 마운트하고, props로 초기화하고, 테스트에서 격리하거나 특정 하위 트리에만 범위를 지정할 수 있습니다.
|
|
71
|
+
|
|
72
|
+
## API
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
function createStore<TStore, TProps = Record<never, never>>(
|
|
76
|
+
useStoreValue: (props: TProps) => TStore,
|
|
77
|
+
): readonly [StoreHook<TStore>, StoreProvider<TProps>]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
반환된 Store Hook에는 객체를 반환하는 selector가 필요합니다. 대응하는 Provider 밖에서 호출하면 명확한 오류가 발생합니다. Strict Mode와 서버 렌더링을 지원합니다.
|
|
81
|
+
|
|
82
|
+
## 문서
|
|
83
|
+
|
|
84
|
+
- [소개](https://violetflux.github.io/kerros/ko/guide/introduction)
|
|
85
|
+
- [시작하기](https://violetflux.github.io/kerros/ko/guide/getting-started)
|
|
86
|
+
- [Selector](https://violetflux.github.io/kerros/ko/guide/selectors)
|
|
87
|
+
- [Store 조합](https://violetflux.github.io/kerros/ko/guide/composition)
|
|
88
|
+
- [API](https://violetflux.github.io/kerros/ko/api/)
|
|
89
|
+
|
|
90
|
+
[MIT License](https://github.com/violetflux/kerros/blob/main/LICENSE)
|
package/README.md
CHANGED
|
@@ -1,39 +1,233 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://violetflux.github.io/kerros/">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/violetflux/kerros/main/docs/public/banner.svg" alt="Kerros — selector-first React stores" width="100%" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
English ·
|
|
9
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.zh-CN.md">简体中文</a> ·
|
|
10
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ja.md">日本語</a> ·
|
|
11
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ko.md">한국어</a> ·
|
|
12
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.de.md">Deutsch</a> ·
|
|
13
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.fr.md">Français</a> ·
|
|
14
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.es.md">Español</a>
|
|
15
|
+
</p>
|
|
4
16
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="https://www.npmjs.com/package/@violetflux/kerros"><img src="https://img.shields.io/npm/v/@violetflux/kerros?color=7c3aed" alt="npm version" /></a>
|
|
19
|
+
<a href="https://github.com/violetflux/kerros/actions/workflows/ci.yml"><img src="https://github.com/violetflux/kerros/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
|
|
20
|
+
<a href="https://bundlephobia.com/package/@violetflux/kerros"><img src="https://img.shields.io/bundlephobia/minzip/@violetflux/kerros?label=gzip&color=2563eb" alt="minified gzip size" /></a>
|
|
21
|
+
<a href="https://github.com/violetflux/kerros/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@violetflux/kerros" alt="MIT license" /></a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
Kerros is a lightweight way to share state between React components.
|
|
25
|
+
|
|
26
|
+
Write a Store the same way you write a custom Hook. When local state needs to be shared, pass it to `createStore`, mount its Provider, and let each component select what it needs.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
| Package manager | Command |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| npm | `npm install @violetflux/kerros` |
|
|
33
|
+
| pnpm | `pnpm add @violetflux/kerros` |
|
|
34
|
+
| Yarn | `yarn add @violetflux/kerros` |
|
|
35
|
+
| Bun | `bun add @violetflux/kerros` |
|
|
36
|
+
|
|
37
|
+
React 17, React 18, and React 19 are supported.
|
|
38
|
+
|
|
39
|
+
## Why Kerros?
|
|
40
|
+
|
|
41
|
+
- **Almost nothing new to learn** — reuse the React knowledge you already have; if you can write a custom Hook, you can write a Store
|
|
42
|
+
- **Designed for flexible refactoring** — Stores and components use the same Hook API, so local state can become shared state with very little work
|
|
43
|
+
- **Local and application-wide state** — Provider placement determines the Store scope, balancing flexibility with simplicity
|
|
44
|
+
- **Performance and TypeScript support** — focused selector subscriptions avoid unrelated rerenders and Store types are inferred automatically
|
|
45
|
+
|
|
46
|
+
## From state management to state sharing
|
|
47
|
+
|
|
48
|
+
Libraries such as Redux, Zustand, and Recoil can all share data, but their central job is still to organize state, update it, and define how data flows. “State management” is the right name for them.
|
|
49
|
+
|
|
50
|
+
Kerros focuses on a smaller and more direct problem. It does not invent a new data model or prescribe how async logic should work. It answers one question: **how can a piece of Hook state be shared between React components?**
|
|
51
|
+
|
|
52
|
+
Passing `value` and `onChange` through layer after layer damages component boundaries. Moving everything into one global Store does not automatically make an application scalable or maintainable either.
|
|
53
|
+
|
|
54
|
+
Kerros stays simple, lightweight, and reliable. Write local state as an ordinary Hook, share it only when necessary, use a Provider to set its scope, and use selectors to choose what each component observes.
|
|
55
|
+
|
|
56
|
+
## Quick start
|
|
57
|
+
|
|
58
|
+
### Create a Store
|
|
59
|
+
|
|
60
|
+
Any custom Hook can become a Kerros Store:
|
|
8
61
|
|
|
9
62
|
```tsx
|
|
10
63
|
import { createStore } from '@violetflux/kerros'
|
|
11
64
|
import { useState } from 'react'
|
|
12
65
|
|
|
13
|
-
|
|
14
|
-
|
|
66
|
+
interface Task {
|
|
67
|
+
id: string
|
|
68
|
+
title: string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const [useTask, TaskProvider] = createStore(() => {
|
|
72
|
+
const [tasks, setTasks] = useState<Task[]>([])
|
|
15
73
|
|
|
16
|
-
|
|
74
|
+
const addTask = (task: Task) => {
|
|
75
|
+
setTasks(v => [...v, task])
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const finishTask = (taskId: string) => {
|
|
79
|
+
setTasks(v => v.filter(task => task.id !== taskId))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { tasks, addTask, finishTask }
|
|
17
83
|
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`createStore` returns two values: the Hook used by components and its matching Provider.
|
|
87
|
+
|
|
88
|
+
The Store is still a normal React Hook. It may use `useState`, `useReducer`, Context, SDK Hooks, or your own custom Hooks.
|
|
18
89
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
90
|
+
### Mount the Provider
|
|
91
|
+
|
|
92
|
+
Only descendants of `TaskProvider` may use `useTask`:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
function App() {
|
|
96
|
+
return (
|
|
97
|
+
<TaskProvider>
|
|
98
|
+
<Header />
|
|
99
|
+
<TaskList />
|
|
100
|
+
</TaskProvider>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Use the Store
|
|
106
|
+
|
|
107
|
+
Pass a selector that returns the fields used by the component:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
function TaskList() {
|
|
111
|
+
const { tasks, finishTask } = useTask(s => ({
|
|
112
|
+
tasks: s.tasks,
|
|
113
|
+
finishTask: s.finishTask,
|
|
23
114
|
}))
|
|
24
115
|
|
|
25
|
-
return
|
|
116
|
+
return (
|
|
117
|
+
<ul>
|
|
118
|
+
{tasks.map(task => (
|
|
119
|
+
<li key={task.id}>
|
|
120
|
+
{task.title}
|
|
121
|
+
<button onClick={() => finishTask(task.id)}>Done</button>
|
|
122
|
+
</li>
|
|
123
|
+
))}
|
|
124
|
+
</ul>
|
|
125
|
+
)
|
|
26
126
|
}
|
|
27
127
|
```
|
|
28
128
|
|
|
29
|
-
Kerros
|
|
129
|
+
Kerros shallowly compares the selector object's top-level fields. When those selected fields stay equal, other Store updates do not rerender `TaskList`. The selector can stay inline and does not need `useCallback`.
|
|
30
130
|
|
|
31
|
-
|
|
131
|
+
## Multiple instances
|
|
32
132
|
|
|
33
|
-
|
|
34
|
-
|
|
133
|
+
Each `TaskProvider` owns independent state:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
<TaskProvider>
|
|
137
|
+
<h2>Personal tasks</h2>
|
|
138
|
+
<TaskList />
|
|
139
|
+
</TaskProvider>
|
|
140
|
+
|
|
141
|
+
<TaskProvider>
|
|
142
|
+
<h2>Team tasks</h2>
|
|
143
|
+
<TaskList />
|
|
144
|
+
</TaskProvider>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Each `TaskList` automatically reads its nearest Provider.
|
|
148
|
+
|
|
149
|
+
## Store dependencies
|
|
150
|
+
|
|
151
|
+
A Store may call another Store directly. For example, a task Store can read the current account:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
export const [useTask, TaskProvider] = createStore(() => {
|
|
155
|
+
const { user } = useAccount(s => ({ user: s.user }))
|
|
156
|
+
const [tasks, setTasks] = useState<Task[]>([])
|
|
157
|
+
|
|
158
|
+
const addTask = (title: string) => {
|
|
159
|
+
if (!user)
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
setTasks(v => [...v, {
|
|
163
|
+
id: crypto.randomUUID(),
|
|
164
|
+
title,
|
|
165
|
+
assigneeId: user.id,
|
|
166
|
+
}])
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return { tasks, addTask }
|
|
170
|
+
})
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Mount Providers in dependency order and keep dependencies one-way:
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
<AccountProvider>
|
|
177
|
+
<TaskProvider>
|
|
178
|
+
<App />
|
|
179
|
+
</TaskProvider>
|
|
180
|
+
</AccountProvider>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Provider props
|
|
184
|
+
|
|
185
|
+
Provider props are passed to the Store Hook:
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
interface CounterProps {
|
|
189
|
+
initialCount: number
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const [useCounter, CounterProvider] = createStore(
|
|
193
|
+
({ initialCount }: CounterProps) => {
|
|
194
|
+
const [count, setCount] = useState(initialCount)
|
|
195
|
+
return { count, setCount }
|
|
196
|
+
},
|
|
197
|
+
)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
<CounterProvider initialCount={42}>
|
|
202
|
+
<Counter />
|
|
203
|
+
</CounterProvider>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## API
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
function createStore<TStore, TProps = Record<never, never>>(
|
|
210
|
+
useStoreValue: (props: TProps) => TStore,
|
|
211
|
+
): readonly [StoreHook<TStore>, StoreProvider<TProps>]
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
- `useStoreValue` follows the Rules of Hooks
|
|
215
|
+
- Provider props, excluding `children`, are passed to `useStoreValue`
|
|
216
|
+
- the returned Store Hook requires an object-returning selector
|
|
217
|
+
- using the Store Hook outside its matching Provider throws a clear error
|
|
218
|
+
- Provider instances work with Strict Mode and server rendering
|
|
219
|
+
|
|
220
|
+
Kerros uses the official `use-sync-external-store` shim for React 17 and prefers React's native implementation in React 18 and 19. React Compiler is optional.
|
|
221
|
+
|
|
222
|
+
## Documentation
|
|
223
|
+
|
|
224
|
+
- [Introduction](https://violetflux.github.io/kerros/guide/introduction)
|
|
225
|
+
- [Quick start](https://violetflux.github.io/kerros/guide/getting-started)
|
|
226
|
+
- [Selectors and rerenders](https://violetflux.github.io/kerros/guide/selectors)
|
|
227
|
+
- [Store dependencies](https://violetflux.github.io/kerros/guide/composition)
|
|
35
228
|
- [Migration from hox](https://violetflux.github.io/kerros/guide/migration)
|
|
229
|
+
- [API reference](https://violetflux.github.io/kerros/api/)
|
|
36
230
|
|
|
37
231
|
## License
|
|
38
232
|
|
|
39
|
-
MIT
|
|
233
|
+
[MIT](https://github.com/violetflux/kerros/blob/main/LICENSE)
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://violetflux.github.io/kerros/zh/">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/violetflux/kerros/main/docs/public/banner.svg" alt="Kerros — selector 优先的 React Store" width="100%" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.md">English</a> ·
|
|
9
|
+
简体中文 ·
|
|
10
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ja.md">日本語</a> ·
|
|
11
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.ko.md">한국어</a> ·
|
|
12
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.de.md">Deutsch</a> ·
|
|
13
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.fr.md">Français</a> ·
|
|
14
|
+
<a href="https://github.com/violetflux/kerros/blob/main/README.es.md">Español</a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
Kerros 是一个在 React 组件间共享状态的轻量方案。
|
|
18
|
+
|
|
19
|
+
你怎么写 custom Hook,就可以怎么写 Store。只有当局部状态需要被多个组件使用时,再交给 `createStore`,用 Provider 决定共享范围,用 selector 选择组件真正需要的数据。
|
|
20
|
+
|
|
21
|
+
## 安装
|
|
22
|
+
|
|
23
|
+
| 包管理器 | 命令 |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| npm | `npm install @violetflux/kerros` |
|
|
26
|
+
| pnpm | `pnpm add @violetflux/kerros` |
|
|
27
|
+
| Yarn | `yarn add @violetflux/kerros` |
|
|
28
|
+
| Bun | `bun add @violetflux/kerros` |
|
|
29
|
+
|
|
30
|
+
支持 React 17、React 18 和 React 19。
|
|
31
|
+
|
|
32
|
+
## 为什么要用 Kerros?
|
|
33
|
+
|
|
34
|
+
- **几乎没有学习成本**:直接复用已有的 React 知识,你怎么写 custom Hook,就可以怎么写 Store
|
|
35
|
+
- **为灵活重构而设计**:Store 和组件使用同一套 Hook API,可以近乎零成本地把组件局部状态转换成组件间共享状态
|
|
36
|
+
- **同时支持局部状态和全局状态**:Provider 决定 Store 的作用域,在灵活和简单之间取得平衡
|
|
37
|
+
- **优秀的性能和 TypeScript 支持**:selector 精确订阅,选择结果不变的组件不会重渲染,Store 类型可以自动推断
|
|
38
|
+
|
|
39
|
+
## 从状态管理到状态共享
|
|
40
|
+
|
|
41
|
+
Redux、Zustand、Recoil 这些状态管理库当然也能解决数据共享问题,但它们最核心的能力仍然是组织数据、操作数据和约束数据流,因此它们应该被称为“状态管理”工具。
|
|
42
|
+
|
|
43
|
+
Kerros 想解决的问题更小,也更直接。它不发明新的数据结构,不规定异步和数据流应该怎么写,只聚焦一个痛点:**如何在多个 React 组件间共享一段 Hook 状态。**
|
|
44
|
+
|
|
45
|
+
层层传递 `value`、`onChange` 会逐渐破坏组件边界;粗暴地把数据全部塞进一个全局 Store,也不会自动让应用获得更好的扩展性和可维护性。
|
|
46
|
+
|
|
47
|
+
Kerros 简单、轻量、可靠。先把状态写成普通 Hook,需要共享时再交给 `createStore`;Provider 决定状态共享到哪里,selector 决定每个组件订阅什么。
|
|
48
|
+
|
|
49
|
+
## 快速上手
|
|
50
|
+
|
|
51
|
+
### 创建 Store
|
|
52
|
+
|
|
53
|
+
任意 custom Hook 都可以变成 Kerros Store:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { createStore } from '@violetflux/kerros'
|
|
57
|
+
import { useState } from 'react'
|
|
58
|
+
|
|
59
|
+
interface Task {
|
|
60
|
+
id: string
|
|
61
|
+
title: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const [useTask, TaskProvider] = createStore(() => {
|
|
65
|
+
const [tasks, setTasks] = useState<Task[]>([])
|
|
66
|
+
|
|
67
|
+
const addTask = (task: Task) => {
|
|
68
|
+
setTasks(v => [...v, task])
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const finishTask = (taskId: string) => {
|
|
72
|
+
setTasks(v => v.filter(task => task.id !== taskId))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { tasks, addTask, finishTask }
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`createStore` 返回两个值:组件调用的 Hook 和对应的 Provider。
|
|
80
|
+
|
|
81
|
+
Store 仍然是普通 React Hook,可以继续使用 `useState`、`useReducer`、Context、SDK Hook 或其他 custom Hook。
|
|
82
|
+
|
|
83
|
+
### 挂载 Provider
|
|
84
|
+
|
|
85
|
+
只有 `TaskProvider` 的子节点可以调用 `useTask`:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
function App() {
|
|
89
|
+
return (
|
|
90
|
+
<TaskProvider>
|
|
91
|
+
<Header />
|
|
92
|
+
<TaskList />
|
|
93
|
+
</TaskProvider>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 使用 Store
|
|
99
|
+
|
|
100
|
+
给 `useTask` 传入 selector,只返回组件使用的字段:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
function TaskList() {
|
|
104
|
+
const { tasks, finishTask } = useTask(s => ({
|
|
105
|
+
tasks: s.tasks,
|
|
106
|
+
finishTask: s.finishTask,
|
|
107
|
+
}))
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<ul>
|
|
111
|
+
{tasks.map(task => (
|
|
112
|
+
<li key={task.id}>
|
|
113
|
+
{task.title}
|
|
114
|
+
<button onClick={() => finishTask(task.id)}>完成</button>
|
|
115
|
+
</li>
|
|
116
|
+
))}
|
|
117
|
+
</ul>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Kerros 会浅比较 selector 返回对象的顶层字段。只要这些选中字段保持不变,Store 的其他更新就不会让 `TaskList` 重渲染。selector 可以直接写在调用位置,不需要 `useCallback`。
|
|
123
|
+
|
|
124
|
+
## 多个实例
|
|
125
|
+
|
|
126
|
+
每个 `TaskProvider` 都拥有独立状态:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
<TaskProvider>
|
|
130
|
+
<h2>个人任务</h2>
|
|
131
|
+
<TaskList />
|
|
132
|
+
</TaskProvider>
|
|
133
|
+
|
|
134
|
+
<TaskProvider>
|
|
135
|
+
<h2>团队任务</h2>
|
|
136
|
+
<TaskList />
|
|
137
|
+
</TaskProvider>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
每个 `TaskList` 会自动读取离自己最近的 Provider。
|
|
141
|
+
|
|
142
|
+
## Store 之间的依赖
|
|
143
|
+
|
|
144
|
+
一个 Store 可以直接调用另一个 Store。例如任务 Store 读取当前账户:
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
export const [useTask, TaskProvider] = createStore(() => {
|
|
148
|
+
const { user } = useAccount(s => ({ user: s.user }))
|
|
149
|
+
const [tasks, setTasks] = useState<Task[]>([])
|
|
150
|
+
|
|
151
|
+
const addTask = (title: string) => {
|
|
152
|
+
if (!user)
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
setTasks(v => [...v, {
|
|
156
|
+
id: crypto.randomUUID(),
|
|
157
|
+
title,
|
|
158
|
+
assigneeId: user.id,
|
|
159
|
+
}])
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return { tasks, addTask }
|
|
163
|
+
})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
按依赖顺序挂 Provider,并保持单向依赖:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<AccountProvider>
|
|
170
|
+
<TaskProvider>
|
|
171
|
+
<App />
|
|
172
|
+
</TaskProvider>
|
|
173
|
+
</AccountProvider>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Provider props
|
|
177
|
+
|
|
178
|
+
Provider props 会传给 Store Hook:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
interface CounterProps {
|
|
182
|
+
initialCount: number
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const [useCounter, CounterProvider] = createStore(
|
|
186
|
+
({ initialCount }: CounterProps) => {
|
|
187
|
+
const [count, setCount] = useState(initialCount)
|
|
188
|
+
return { count, setCount }
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
<CounterProvider initialCount={42}>
|
|
195
|
+
<Counter />
|
|
196
|
+
</CounterProvider>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## API
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
function createStore<TStore, TProps = Record<never, never>>(
|
|
203
|
+
useStoreValue: (props: TProps) => TStore,
|
|
204
|
+
): readonly [StoreHook<TStore>, StoreProvider<TProps>]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
- `useStoreValue` 必须遵守 Hooks 规则
|
|
208
|
+
- 除 `children` 外的 Provider props 会传给 `useStoreValue`
|
|
209
|
+
- Store Hook 必须接收一个返回对象的 selector
|
|
210
|
+
- 在对应 Provider 外调用会抛出明确错误
|
|
211
|
+
- 支持 Strict Mode、服务端渲染和 Provider 多实例
|
|
212
|
+
|
|
213
|
+
React 17 使用官方 `use-sync-external-store` shim;React 18 和 19 可用时优先使用 React 原生实现。React Compiler 不是必需项。
|
|
214
|
+
|
|
215
|
+
## 文档
|
|
216
|
+
|
|
217
|
+
- [介绍](https://violetflux.github.io/kerros/zh/guide/introduction)
|
|
218
|
+
- [快速上手](https://violetflux.github.io/kerros/zh/guide/getting-started)
|
|
219
|
+
- [Selector 与重渲染](https://violetflux.github.io/kerros/zh/guide/selectors)
|
|
220
|
+
- [Store 之间的依赖](https://violetflux.github.io/kerros/zh/guide/composition)
|
|
221
|
+
- [从 hox 迁移](https://violetflux.github.io/kerros/zh/guide/migration)
|
|
222
|
+
- [API 参考](https://violetflux.github.io/kerros/zh/api/)
|
|
223
|
+
|
|
224
|
+
## 许可证
|
|
225
|
+
|
|
226
|
+
[MIT](https://github.com/violetflux/kerros/blob/main/LICENSE)
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@violetflux/kerros",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Hook-native state sharing for React with focused selector subscriptions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
|
+
"react-hooks",
|
|
8
|
+
"react-context",
|
|
7
9
|
"state-management",
|
|
8
10
|
"store",
|
|
9
11
|
"selector",
|
|
12
|
+
"external-store",
|
|
10
13
|
"use-sync-external-store"
|
|
11
14
|
],
|
|
12
15
|
"homepage": "https://violetflux.github.io/kerros/",
|