@yoltra/react 0.6.0 → 0.8.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.
- package/README.es.md +120 -74
- package/README.md +85 -39
- package/dist/index.cjs +4 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +509 -440
- package/dist/index.mjs.map +1 -1
- package/dist/types/createYoltra.d.ts +64 -3
- package/dist/types/hooks/createHooks.d.ts +11 -1
- package/dist/types/hooks/hooks.d.ts +11 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +8 -6
package/README.es.md
CHANGED
|
@@ -3,22 +3,24 @@
|
|
|
3
3
|
# @yoltra/react
|
|
4
4
|
|
|
5
5
|
> 👉 🇲🇽 Versión en Español |
|
|
6
|
-
> [ 🇺🇸 English
|
|
6
|
+
> [ 🇺🇸 English Versión](./README.md)
|
|
7
7
|
|
|
8
|
-
](https://www.npmjs.com/package/@yoltra/react)
|
|
9
|
+
[](https://www.npmjs.com/package/@yoltra/react)
|
|
10
|
+
[](https://www.npmjs.com/package/@yoltra/react)
|
|
11
|
+
[](https://github.com/yoltra/yoltra/blob/main/LICENSE)
|
|
10
12
|
|
|
11
13
|
**Hooks de React para [yoltra](../../README.md) con
|
|
12
14
|
suscripciones de grano fino por ruta.**
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
esa ruta exacta cambia. Sin selectores, sin
|
|
16
|
+
Suscríbete a `"items.0.title"` o `"items.*.done"`. El componente se re-renderiza solo cuando
|
|
17
|
+
esa ruta exacta cambia. Sin selectores, sin memoización, sin optimización manual.
|
|
16
18
|
|
|
17
|
-
[Ver la
|
|
19
|
+
[Ver la comparación de flamegraph (Redux vs yoltra).](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-in-react/redux-yoltra-profiler.md)
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
|
21
|
-
##
|
|
23
|
+
## Instalación
|
|
22
24
|
|
|
23
25
|
```bash
|
|
24
26
|
npm install @yoltra/core @yoltra/react
|
|
@@ -28,11 +30,11 @@ npm install @yoltra/core @yoltra/react
|
|
|
28
30
|
|
|
29
31
|
---
|
|
30
32
|
|
|
31
|
-
##
|
|
33
|
+
## Configuración con `createYoltra` (recomendado)
|
|
32
34
|
|
|
33
|
-
`createYoltra` crea el store **y** todos los hooks tipados en una sola llamada
|
|
34
|
-
context aparte, sin cableado de `createHooks`, sin provider obligatorio. Todos los
|
|
35
|
-
tipo se infieren de tu reducer,
|
|
35
|
+
`createYoltra` crea el store **y** todos los hooks tipados en una sola llamada, sin archivo de
|
|
36
|
+
context aparte, sin cableado de `createHooks`, sin provider obligatorio. Todos los parámetros de
|
|
37
|
+
tipo se infieren de tu reducer, así que los componentes no necesitan generics explícitos.
|
|
36
38
|
|
|
37
39
|
### 1. Crea el store y los hooks
|
|
38
40
|
|
|
@@ -74,10 +76,10 @@ export const { store, useAtomicProp, useEmit, StoreProvider } = createYoltra({
|
|
|
74
76
|
});
|
|
75
77
|
```
|
|
76
78
|
|
|
77
|
-
### 2. Usa los hooks
|
|
79
|
+
### 2. Usa los hooks, sin provider
|
|
78
80
|
|
|
79
|
-
Los hooks usan por defecto el store de arriba,
|
|
80
|
-
|
|
81
|
+
Los hooks usan por defecto el store de arriba, así que puedes renderizar componentes directamente.
|
|
82
|
+
Suscríbete con una spec **`{ reducer, property }`**: el `property` con puntos nombra la ruta exacta
|
|
81
83
|
a leer.
|
|
82
84
|
|
|
83
85
|
```tsx
|
|
@@ -85,7 +87,7 @@ a leer.
|
|
|
85
87
|
import { useAtomicProp, useEmit } from "./yoltra";
|
|
86
88
|
|
|
87
89
|
export function Counter() {
|
|
88
|
-
// Forma objeto
|
|
90
|
+
// Forma objeto: se re-renderiza solo cuando counter.value cambia. Sin selectores, sin memo.
|
|
89
91
|
const value = useAtomicProp({ reducer: "counter", property: "value" });
|
|
90
92
|
const emit = useEmit();
|
|
91
93
|
|
|
@@ -101,13 +103,13 @@ export function Counter() {
|
|
|
101
103
|
```
|
|
102
104
|
|
|
103
105
|
Un `<StoreProvider>` solo se necesita para acotar una instancia **diferente** del store a un
|
|
104
|
-
subarbol (p. ej. un store nuevo por test)
|
|
106
|
+
subarbol (p. ej. un store nuevo por test). `createYoltra` devuelve uno justo para eso.
|
|
105
107
|
|
|
106
108
|
---
|
|
107
109
|
|
|
108
110
|
## Avanzado: cableado manual con `createHooks`
|
|
109
111
|
|
|
110
|
-
Cuando necesites un mismo conjunto de hooks compartido entre varias instancias de store a
|
|
112
|
+
Cuando necesites un mismo conjunto de hooks compartido entre varias instancias de store a través
|
|
111
113
|
de tu propio context de React, vinculalos tu mismo con `createHooks(context)`. `createYoltra` es
|
|
112
114
|
este mismo cableado colapsado en una sola llamada.
|
|
113
115
|
|
|
@@ -135,7 +137,41 @@ export const {
|
|
|
135
137
|
} = createHooks(AppStoreContext);
|
|
136
138
|
```
|
|
137
139
|
|
|
138
|
-
Provee el store con `<AppStoreContext.Provider value={store}>` en tu
|
|
140
|
+
Provee el store con `<AppStoreContext.Provider value={store}>` en tu raíz.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Agregar a un store, con sus tipos
|
|
145
|
+
|
|
146
|
+
Una librería puede montar una slice en un store que no creó, y los hooks crecen para conocerla.
|
|
147
|
+
`withSlice`, `withMiddleware` y `withEffect` devuelven un `Yoltra` cuyos tipos se ampliaron.
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import { defineSlice } from "@yoltra/core";
|
|
151
|
+
|
|
152
|
+
// Ámbito de módulo, una sola vez, antes del primer render.
|
|
153
|
+
export const app = createYoltra({ name: "App", reducer: { counter } })
|
|
154
|
+
.withSlice("transfers", defineSlice<TransferEM>()({ ... }));
|
|
155
|
+
|
|
156
|
+
export const { useAtomicProp, useEmit } = app;
|
|
157
|
+
|
|
158
|
+
// Tipado, sobre una slice que la aplicación nunca declaró.
|
|
159
|
+
const granted = useAtomicProp({ reducer: "transfers", property: "granted" });
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Tres cosas que conviene saber:
|
|
163
|
+
|
|
164
|
+
- **Ámbito de módulo, una vez, antes del primer render.** Cada llamada construye un conjunto
|
|
165
|
+
nuevo de hooks, porque `createHooks` asigna funciones nuevas. Llamarla dentro de un componente
|
|
166
|
+
le daría a React un `useAtomicProp` distinto en cada render.
|
|
167
|
+
- **El store y el contexto son los mismos objetos.** Solo cambian los tipos, así que un
|
|
168
|
+
`<StoreProvider>` de cualquier vista de la cadena sirve a los hooks de todas las demás, y la
|
|
169
|
+
caché de Suspense se comparte.
|
|
170
|
+
- **También existen las funciones libres**, para una librería que recibe un `Yoltra` que no creó:
|
|
171
|
+
`withSlice(yoltra, name, spec)`.
|
|
172
|
+
|
|
173
|
+
El contrato completo, incluido qué hacer con la disposición, está en la
|
|
174
|
+
[guía de decoración](https://github.com/yoltra/yoltra/blob/main/docs/es/DECORATION_GUIDE.md).
|
|
139
175
|
|
|
140
176
|
---
|
|
141
177
|
|
|
@@ -143,21 +179,21 @@ Provee el store con `<AppStoreContext.Provider value={store}>` en tu raiz.
|
|
|
143
179
|
|
|
144
180
|
### `useAtomicProp({ reducer, property }, map?, isEqual?)`
|
|
145
181
|
|
|
146
|
-
Selector de ruta
|
|
147
|
-
`property` con puntos nombra la ruta exacta
|
|
182
|
+
Selector de ruta única con grano fino. Se re-renderiza solo cuando la hoja especificada cambia. El
|
|
183
|
+
`property` con puntos nombra la ruta exacta, incluyendo rutas dinámicas
|
|
148
184
|
(`` `items.${id}.title` ``) y con comodines.
|
|
149
185
|
|
|
150
186
|
```tsx
|
|
151
|
-
// Forma objeto (recomendada)
|
|
187
|
+
// Forma objeto (recomendada): suscribete a la ruta exacta
|
|
152
188
|
const title = useAtomicProp({ reducer: "todos", property: "items.0.title" });
|
|
153
189
|
|
|
154
|
-
// Ruta dinamica
|
|
190
|
+
// Ruta dinamica: interpola la clave
|
|
155
191
|
const byId = useAtomicProp({ reducer: "todos", property: `items.${id}.title` });
|
|
156
192
|
|
|
157
|
-
// Con mapper
|
|
193
|
+
// Con mapper: derivar un valor de la ruta
|
|
158
194
|
const count = useAtomicProp({ reducer: "todos", property: "items" }, (items) => items.length);
|
|
159
195
|
|
|
160
|
-
// Patron wildcard
|
|
196
|
+
// Patron wildcard: se re-renderiza cuando cualquier item cambia
|
|
161
197
|
const allTitles = useAtomicProp(
|
|
162
198
|
{ reducer: "todos", property: "items.**" },
|
|
163
199
|
(state) => state.items.map((t) => t.title),
|
|
@@ -165,20 +201,20 @@ const allTitles = useAtomicProp(
|
|
|
165
201
|
);
|
|
166
202
|
```
|
|
167
203
|
|
|
168
|
-
>
|
|
169
|
-
>
|
|
204
|
+
> También existe una sobrecarga con accessor tipado, `useAtomicProp("todos", (s) => s.items[0].title)`,
|
|
205
|
+
> para rutas estáticas; autocompleta la forma del estado e infiere el tipo de retorno.
|
|
170
206
|
|
|
171
207
|
**Patrones soportados:**
|
|
172
208
|
|
|
173
|
-
- `"items.0.title"
|
|
174
|
-
- `"items.*.title"
|
|
175
|
-
- `"items.**"
|
|
209
|
+
- `"items.0.title"`: ruta exacta (incluyendo índices numéricos de array)
|
|
210
|
+
- `"items.*.title"`: `*` coincide con un segmento
|
|
211
|
+
- `"items.**"`: `**` coincide con cero o más segmentos
|
|
176
212
|
|
|
177
213
|
---
|
|
178
214
|
|
|
179
215
|
### `useAtomicProps(specs, selector, isEqual?)`
|
|
180
216
|
|
|
181
|
-
Selector de
|
|
217
|
+
Selector de múltiples rutas. Se suscribe a varias rutas y recalcula cuando alguna cambia.
|
|
182
218
|
|
|
183
219
|
```tsx
|
|
184
220
|
const filtered = useAtomicProps(
|
|
@@ -193,18 +229,17 @@ const filtered = useAtomicProps(
|
|
|
193
229
|
|
|
194
230
|
---
|
|
195
231
|
|
|
196
|
-
### `useEvent(channel, type, handler, phase?)`
|
|
232
|
+
### `useEvent(channel, type, handler, phase?, options?)`
|
|
197
233
|
|
|
198
|
-
|
|
199
|
-
fire-and-forget.
|
|
234
|
+
Suscríbete a eventos del store desde un componente. No afecta el flujo de eventos. Es fire-and-forget.
|
|
200
235
|
|
|
201
236
|
```tsx
|
|
202
|
-
// Eventos confirmados (por defecto)
|
|
237
|
+
// Eventos confirmados (por defecto): eventos que pasaron el middleware
|
|
203
238
|
useEvent("ui", "save", (event) => {
|
|
204
239
|
showToast("Saved!");
|
|
205
240
|
});
|
|
206
241
|
|
|
207
|
-
// Eventos no confirmados
|
|
242
|
+
// Eventos no confirmados: eventos rechazados por el middleware
|
|
208
243
|
useEvent(
|
|
209
244
|
"ui",
|
|
210
245
|
"delete",
|
|
@@ -214,7 +249,7 @@ useEvent(
|
|
|
214
249
|
"uncommitted",
|
|
215
250
|
);
|
|
216
251
|
|
|
217
|
-
// Todos los eventos
|
|
252
|
+
// Todos los eventos: distinguir por fase
|
|
218
253
|
useEvent(
|
|
219
254
|
"ui",
|
|
220
255
|
"action",
|
|
@@ -227,15 +262,26 @@ useEvent(
|
|
|
227
262
|
|
|
228
263
|
**Fases:**
|
|
229
264
|
|
|
230
|
-
- `'committed'` (por defecto)
|
|
231
|
-
- `'uncommitted'
|
|
232
|
-
- `'
|
|
265
|
+
- `'committed'` (por defecto): eventos que pasaron el middleware y llegaron a los reducers
|
|
266
|
+
- `'uncommitted'`: eventos rechazados por el middleware
|
|
267
|
+
- `'written'`: eventos que de verdad cambiaron el estado
|
|
268
|
+
- `'all'`: committed y uncommitted, con parámetro `phase` para distinguir
|
|
269
|
+
|
|
270
|
+
**Viaje en el tiempo.** Un handler **no** se ejecuta mientras DevTools está haciendo replay.
|
|
271
|
+
Recorrer una línea de tiempo volvía a ejecutar cada handler igual que un evento real, lo que
|
|
272
|
+
significaba volver a publicar, volver a escribir y volver a disparar analítica por eventos que no
|
|
273
|
+
estaban ocurriendo de nuevo. Actívalo solo para un handler que derive estado de vista del flujo
|
|
274
|
+
de eventos y no haga E/S:
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
useEvent("ui", "save", handler, "committed", { duringReplay: true });
|
|
278
|
+
```
|
|
233
279
|
|
|
234
280
|
---
|
|
235
281
|
|
|
236
282
|
### `useEmit()`
|
|
237
283
|
|
|
238
|
-
Retorna la
|
|
284
|
+
Retorna la función `emit` tipada del store (referencia estable).
|
|
239
285
|
|
|
240
286
|
```tsx
|
|
241
287
|
const emit = useEmit();
|
|
@@ -246,7 +292,7 @@ await emit("counter", "increment", 1);
|
|
|
246
292
|
|
|
247
293
|
### `useSelector(selector, isEqual?)`
|
|
248
294
|
|
|
249
|
-
Selector de grano grueso
|
|
295
|
+
Selector de grano grueso vía `useSyncExternalStore`. Se re-renderiza cuando el valor
|
|
250
296
|
seleccionado cambia.
|
|
251
297
|
|
|
252
298
|
```tsx
|
|
@@ -270,7 +316,7 @@ const value = store.getState().counter.value;
|
|
|
270
316
|
```
|
|
271
317
|
|
|
272
318
|
`getState()` es una lectura, no una suscripción. Llamado durante el render, el componente se
|
|
273
|
-
renderiza una vez con ese valor y nunca más
|
|
319
|
+
renderiza una vez con ese valor y nunca más, porque nada le avisó de que el valor cambió. Parece que
|
|
274
320
|
funciona hasta que el estado cambia y la pantalla no. Lee con `useAtomicProp` o `useSelector` lo
|
|
275
321
|
que vayas a renderizar, y deja `getState()` para callbacks y efectos, que es para lo que es.
|
|
276
322
|
|
|
@@ -280,8 +326,8 @@ que vayas a renderizar, y deja `getState()` para callbacks y efectos, que es par
|
|
|
280
326
|
|
|
281
327
|
### `useSuspenseAtomicProp(spec, options)`
|
|
282
328
|
|
|
283
|
-
|
|
284
|
-
por el boundary `<Suspense>`
|
|
329
|
+
Versión compatible con Suspense de `useAtomicProp`. Lanza una promesa mientras carga, capturada
|
|
330
|
+
por el boundary `<Suspense>` más cercano.
|
|
285
331
|
|
|
286
332
|
```tsx
|
|
287
333
|
function UserName({ userId }: { userId: string }) {
|
|
@@ -303,7 +349,7 @@ function UserName({ userId }: { userId: string }) {
|
|
|
303
349
|
|
|
304
350
|
### `useSuspenseAtomicProps(specs, options)`
|
|
305
351
|
|
|
306
|
-
Selector Suspense de
|
|
352
|
+
Selector Suspense de múltiples rutas.
|
|
307
353
|
|
|
308
354
|
```tsx
|
|
309
355
|
const stats = useSuspenseAtomicProps(
|
|
@@ -318,11 +364,11 @@ const stats = useSuspenseAtomicProps(
|
|
|
318
364
|
### Importalos de tu conjunto de hooks, no del barrel
|
|
319
365
|
|
|
320
366
|
`createYoltra` y `createHooks` devuelven estos dos junto con el resto, ligados al mismo contexto.
|
|
321
|
-
Deliberadamente **no** se exportan desde el barrel del paquete: una copia a nivel de paquete
|
|
322
|
-
|
|
323
|
-
|
|
367
|
+
Deliberadamente **no** se exportan desde el barrel del paquete: una copia a nivel de paquete sería
|
|
368
|
+
idéntica en forma y aun así lanzaría `[yoltra] No store in context` en tiempo de
|
|
369
|
+
ejecución cuando el contexto que lee nunca se lleno, un error que los tipos no podian atrapar.
|
|
324
370
|
Importarlos desde cualquier sitio que no sea el resultado de tu propio `createYoltra`/`createHooks`
|
|
325
|
-
es ahora un error de
|
|
371
|
+
es ahora un error de compilación, que es el mismo aviso llegando en el momento correcto.
|
|
326
372
|
|
|
327
373
|
```tsx
|
|
328
374
|
// store.ts
|
|
@@ -332,8 +378,8 @@ export const { store, useAtomicProp, useSuspenseAtomicProp } = createYoltra({ ..
|
|
|
332
378
|
import { useSuspenseAtomicProp } from "./store"; // ✅ conoce el store
|
|
333
379
|
```
|
|
334
380
|
|
|
335
|
-
Los valores en cache tienen alcance por store,
|
|
336
|
-
y ruta mantienen entradas separadas; las utilidades de
|
|
381
|
+
Los valores en cache tienen alcance por store, así que dos stores que compartan nombre de reducer
|
|
382
|
+
y ruta mantienen entradas separadas; las utilidades de invalidación de abajo reciben una ruta y la
|
|
337
383
|
limpian en todos los stores que la hayan cacheado.
|
|
338
384
|
|
|
339
385
|
### Utilidades de cache
|
|
@@ -359,7 +405,7 @@ clearSuspenseCache();
|
|
|
359
405
|
|
|
360
406
|
## `shallowEqual`
|
|
361
407
|
|
|
362
|
-
Comparador de igualdad superficial de objetos.
|
|
408
|
+
Comparador de igualdad superficial de objetos. Úsalo como argumento `isEqual` cuando tu valor
|
|
363
409
|
derivado es un objeto plano:
|
|
364
410
|
|
|
365
411
|
```tsx
|
|
@@ -372,7 +418,7 @@ const todos = useAtomicProp(
|
|
|
372
418
|
|
|
373
419
|
---
|
|
374
420
|
|
|
375
|
-
## Rendimiento: Antes y
|
|
421
|
+
## Rendimiento: Antes y Después
|
|
376
422
|
|
|
377
423
|
### Antes (grano grueso)
|
|
378
424
|
|
|
@@ -384,7 +430,7 @@ function TodoList() {
|
|
|
384
430
|
}
|
|
385
431
|
```
|
|
386
432
|
|
|
387
|
-
###
|
|
433
|
+
### Después (grano fino con yoltra)
|
|
388
434
|
|
|
389
435
|
```tsx
|
|
390
436
|
// Cada TodoItem se re-renderiza SOLO cuando sus propios datos cambian
|
|
@@ -401,14 +447,14 @@ function TodoItem({ index }: { index: number }) {
|
|
|
401
447
|
}
|
|
402
448
|
```
|
|
403
449
|
|
|
404
|
-
[Ver la
|
|
450
|
+
[Ver la comparación completa de flamegraph.](https://github.com/yoltra/yoltra/blob/main/examples/v0/yoltra-in-react/redux-yoltra-profiler.md)
|
|
405
451
|
|
|
406
452
|
---
|
|
407
453
|
|
|
408
454
|
## Compatibilidad con React 18+
|
|
409
455
|
|
|
410
456
|
- **Concurrent Mode:** Totalmente compatible. Todos los hooks usan `useSyncExternalStore`.
|
|
411
|
-
- **Strict Mode:** La
|
|
457
|
+
- **Strict Mode:** La deduplicación de eventos previene el doble procesamiento.
|
|
412
458
|
- **Suspense:** `useSuspenseAtomicProp` y `useSuspenseAtomicProps` lanzan promesas para
|
|
413
459
|
boundaries `<Suspense>`.
|
|
414
460
|
|
|
@@ -416,37 +462,37 @@ function TodoItem({ index }: { index: number }) {
|
|
|
416
462
|
|
|
417
463
|
## Ejemplos
|
|
418
464
|
|
|
419
|
-
- **[App de Tareas con Profiler](
|
|
420
|
-
|
|
421
|
-
- **[Logo
|
|
465
|
+
- **[App de Tareas con Profiler](https://github.com/yoltra/yoltra/tree/main/examples/v0/yoltra-in-react)**: CRUD completo con
|
|
466
|
+
comparación de flamegraph · [▶ Abrir la demo en vivo](https://yoltra.dev/es/demos/in-react)
|
|
467
|
+
- **[Logo Cinético (3000 particulas)](https://github.com/yoltra/yoltra/tree/main/examples/v0/yoltra-kinetic-logo)**: Suscripciones
|
|
422
468
|
independientes por circulo SVG · [▶ Abrir la demo en vivo](https://yoltra.dev/es/demos/kinetic-logo)
|
|
423
|
-
- **[Next.js (Pages Router)](
|
|
469
|
+
- **[Next.js (Pages Router)](https://github.com/yoltra/yoltra/tree/main/examples/v0/yoltra-in-nextjs)**: estado de cliente + cambio de tema · [▶ Abrir la demo en vivo](https://yoltra.dev/es/demos/in-nextjs)
|
|
424
470
|
|
|
425
471
|
---
|
|
426
472
|
|
|
427
|
-
##
|
|
473
|
+
## Documentación
|
|
428
474
|
|
|
429
|
-
- **[README
|
|
430
|
-
|
|
431
|
-
- **[API de @yoltra/core](../core/README.md)
|
|
432
|
-
|
|
433
|
-
- **[Guia de Inicio
|
|
434
|
-
|
|
435
|
-
- **[
|
|
436
|
-
|
|
475
|
+
- **[README raíz de yoltra](../../README.md)**:
|
|
476
|
+
Descripción general y configuración rápida
|
|
477
|
+
- **[API de @yoltra/core](../core/README.md)**:
|
|
478
|
+
Store, middleware, efectos, matchers `When`
|
|
479
|
+
- **[Guia de Inicio Rápido](https://github.com/yoltra/yoltra/blob/main/docs/en/QUICK_START_GUIDE.md)**:
|
|
480
|
+
Cinco pasos hacia una app funcional
|
|
481
|
+
- **[Comparación de Bibliotecas](https://github.com/yoltra/yoltra/blob/main/docs/en/design/state-management-library-comparison.md)**:
|
|
482
|
+
Comparación arquitectónica
|
|
437
483
|
|
|
438
484
|
---
|
|
439
485
|
|
|
440
486
|
## Contribuir
|
|
441
487
|
|
|
442
|
-
- [
|
|
443
|
-
- [Guia de
|
|
488
|
+
- [Raíz del Monorepo](https://github.com/yoltra/yoltra)
|
|
489
|
+
- [Guia de Contribución](../../CONTRIBUTING.md)
|
|
444
490
|
|
|
445
491
|
---
|
|
446
492
|
|
|
447
493
|
## Estado
|
|
448
494
|
|
|
449
|
-
**Release Candidate
|
|
495
|
+
**Release Candidate**. Las APIs son estables, usadas en producción, cambios menores posibles
|
|
450
496
|
antes de v1.0.0.
|
|
451
497
|
|
|
452
498
|
---
|
|
@@ -454,7 +500,7 @@ antes de v1.0.0.
|
|
|
454
500
|
## Colecciones normalizadas
|
|
455
501
|
|
|
456
502
|
`useEntityIds`, `useEntity` y `useEntityField` se emparejan con `createEntityAdapter` de
|
|
457
|
-
`@yoltra/core`. Son envoltorios delgados sobre `useAtomicProp`; el valor
|
|
503
|
+
`@yoltra/core`. Son envoltorios delgados sobre `useAtomicProp`; el valor está en que la ruta viene
|
|
458
504
|
del adapter en vez de escribirse a mano en un componente, donde nada la verifica.
|
|
459
505
|
|
|
460
506
|
```tsx
|
|
@@ -472,4 +518,4 @@ function Row({ id }: { id: string }) {
|
|
|
472
518
|
|
|
473
519
|
## Licencia
|
|
474
520
|
|
|
475
|
-
**MIT
|
|
521
|
+
**MIT**. Libre para usar en proyectos comerciales y de código abierto.
|