@riktajs/react 0.11.5 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -135
- package/dist/index.cjs +115 -203
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -144
- package/dist/index.d.ts +152 -144
- package/dist/index.js +113 -200
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# @riktajs/react
|
|
2
2
|
|
|
3
|
-
React utilities for Rikta SSR framework. Provides hooks
|
|
3
|
+
React utilities for Rikta SSR framework. Provides hooks for SSR data access, navigation, and server interactions using **native browser APIs**.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
This package embraces the web platform by using native browser APIs for navigation:
|
|
8
|
+
- Use standard `<a href="...">` tags for links
|
|
9
|
+
- Use `useNavigate()` for programmatic navigation (uses `window.location` under the hood)
|
|
10
|
+
- Full page loads ensure SSR data is always fresh
|
|
4
11
|
|
|
5
12
|
## Installation
|
|
6
13
|
|
|
@@ -30,37 +37,56 @@ hydrateRoot(
|
|
|
30
37
|
);
|
|
31
38
|
```
|
|
32
39
|
|
|
33
|
-
##
|
|
40
|
+
## Navigation
|
|
34
41
|
|
|
35
|
-
###
|
|
42
|
+
### Using Native Links
|
|
36
43
|
|
|
37
|
-
|
|
44
|
+
Use standard HTML anchor tags for navigation:
|
|
38
45
|
|
|
39
46
|
```tsx
|
|
40
|
-
import { Link } from '@riktajs/react';
|
|
41
|
-
|
|
42
47
|
function Navigation() {
|
|
43
48
|
return (
|
|
44
49
|
<nav>
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
{/* With options */}
|
|
50
|
-
<Link href="/dashboard" replace scroll={false}>
|
|
51
|
-
Dashboard
|
|
52
|
-
</Link>
|
|
50
|
+
<a href="/">Home</a>
|
|
51
|
+
<a href="/about">About</a>
|
|
52
|
+
<a href="/items/123">Item 123</a>
|
|
53
53
|
</nav>
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
### Programmatic Navigation with `useNavigate()`
|
|
59
|
+
|
|
60
|
+
For programmatic navigation, use the `useNavigate()` hook:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { useNavigate } from '@riktajs/react';
|
|
64
|
+
|
|
65
|
+
function MyComponent() {
|
|
66
|
+
const navigate = useNavigate();
|
|
67
|
+
|
|
68
|
+
const handleSubmit = async () => {
|
|
69
|
+
await saveData();
|
|
70
|
+
// Simple navigation
|
|
71
|
+
navigate('/success');
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const handleSearch = (query: string) => {
|
|
75
|
+
// Navigation with query params - easy!
|
|
76
|
+
navigate('/search', { q: query, page: 1 });
|
|
77
|
+
// Results in: /search?q=query&page=1
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const handleLogin = () => {
|
|
81
|
+
// Replace history entry (for redirects)
|
|
82
|
+
navigate('/dashboard', { replace: true });
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return <button onClick={handleSubmit}>Submit</button>;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Components
|
|
64
90
|
|
|
65
91
|
### `<RiktaProvider>`
|
|
66
92
|
|
|
@@ -80,27 +106,28 @@ function App() {
|
|
|
80
106
|
|
|
81
107
|
## Hooks
|
|
82
108
|
|
|
83
|
-
### `
|
|
109
|
+
### `useNavigate()`
|
|
84
110
|
|
|
85
|
-
Programmatic navigation hook.
|
|
111
|
+
Programmatic navigation hook using native browser APIs.
|
|
86
112
|
|
|
87
113
|
```tsx
|
|
88
|
-
import {
|
|
114
|
+
import { useNavigate } from '@riktajs/react';
|
|
89
115
|
|
|
90
116
|
function MyComponent() {
|
|
91
|
-
const
|
|
117
|
+
const navigate = useNavigate();
|
|
92
118
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
navigate('/success');
|
|
96
|
-
};
|
|
119
|
+
// Simple navigation
|
|
120
|
+
navigate('/dashboard');
|
|
97
121
|
|
|
98
|
-
// With
|
|
122
|
+
// With query params
|
|
123
|
+
navigate('/search', { q: 'hello', page: 1 });
|
|
124
|
+
// Results in: /search?q=hello&page=1
|
|
125
|
+
|
|
126
|
+
// Replace history (no back button)
|
|
99
127
|
navigate('/login', { replace: true });
|
|
100
|
-
navigate('/next', { scroll: false });
|
|
101
|
-
navigate('/edit', { state: { from: 'list' } });
|
|
102
128
|
|
|
103
|
-
|
|
129
|
+
// Params + options
|
|
130
|
+
navigate('/items', { filter: 'active' }, { replace: true });
|
|
104
131
|
}
|
|
105
132
|
```
|
|
106
133
|
|
|
@@ -139,7 +166,7 @@ function SearchPage() {
|
|
|
139
166
|
const page = parseInt(searchParams.get('page') ?? '1', 10);
|
|
140
167
|
|
|
141
168
|
const handleSearch = (newQuery: string) => {
|
|
142
|
-
setSearchParams({ q: newQuery, page:
|
|
169
|
+
setSearchParams({ q: newQuery, page: 1 });
|
|
143
170
|
};
|
|
144
171
|
|
|
145
172
|
return (
|
|
@@ -153,7 +180,7 @@ function SearchPage() {
|
|
|
153
180
|
|
|
154
181
|
### `useLocation()`
|
|
155
182
|
|
|
156
|
-
|
|
183
|
+
Access current location information directly from `window.location`.
|
|
157
184
|
|
|
158
185
|
```tsx
|
|
159
186
|
import { useLocation } from '@riktajs/react';
|
|
@@ -172,34 +199,28 @@ function Breadcrumbs() {
|
|
|
172
199
|
|
|
173
200
|
### `useSsrData()`
|
|
174
201
|
|
|
175
|
-
Access SSR data passed from server via `window.__SSR_DATA__`.
|
|
176
|
-
- `data`: The actual page data from the controller
|
|
177
|
-
- `url`: The current URL
|
|
178
|
-
- `title`: Page title (from `@Ssr` decorator)
|
|
179
|
-
- `description`: Page description (from `@Ssr` decorator)
|
|
202
|
+
Access SSR data passed from the server via `window.__SSR_DATA__`.
|
|
180
203
|
|
|
181
204
|
```tsx
|
|
182
205
|
import { useSsrData } from '@riktajs/react';
|
|
183
206
|
|
|
184
207
|
interface PageData {
|
|
185
|
-
|
|
208
|
+
title: string;
|
|
186
209
|
items: Array<{ id: string; name: string }>;
|
|
187
210
|
}
|
|
188
211
|
|
|
189
212
|
function ItemList() {
|
|
190
213
|
const ssrData = useSsrData<PageData>();
|
|
191
214
|
|
|
192
|
-
if (!ssrData)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const { data, title, url } = ssrData;
|
|
215
|
+
if (!ssrData) {
|
|
216
|
+
return <div>Loading...</div>;
|
|
217
|
+
}
|
|
196
218
|
|
|
197
219
|
return (
|
|
198
220
|
<div>
|
|
199
|
-
<h1>{
|
|
200
|
-
<p>Current URL: {url}</p>
|
|
221
|
+
<h1>{ssrData.data.title}</h1>
|
|
201
222
|
<ul>
|
|
202
|
-
{data.items.map(item => (
|
|
223
|
+
{ssrData.data.items.map(item => (
|
|
203
224
|
<li key={item.id}>{item.name}</li>
|
|
204
225
|
))}
|
|
205
226
|
</ul>
|
|
@@ -208,34 +229,9 @@ function ItemList() {
|
|
|
208
229
|
}
|
|
209
230
|
```
|
|
210
231
|
|
|
211
|
-
### Client-Side Navigation with SSR Data Fetching
|
|
212
|
-
|
|
213
|
-
When navigating client-side using `<Link>` or `navigate()`, `RiktaProvider` automatically fetches the SSR data for the new page from the server. This ensures:
|
|
214
|
-
|
|
215
|
-
1. **No page flash**: Data is fetched before the route changes
|
|
216
|
-
2. **Consistent data structure**: Same data shape as initial SSR
|
|
217
|
-
3. **SEO metadata**: Title and description are updated automatically
|
|
218
|
-
|
|
219
|
-
```tsx
|
|
220
|
-
// App.tsx - Route based on ssrData.url for data consistency
|
|
221
|
-
function App() {
|
|
222
|
-
const ssrData = useSsrData<{ page: string }>();
|
|
223
|
-
|
|
224
|
-
// Use ssrData.url for routing to ensure data and route are in sync
|
|
225
|
-
const pathname = ssrData?.url?.split('?')[0] ?? '/';
|
|
226
|
-
|
|
227
|
-
return (
|
|
228
|
-
<Layout title={ssrData?.title}>
|
|
229
|
-
{pathname === '/about' && <AboutPage />}
|
|
230
|
-
{pathname === '/' && <HomePage />}
|
|
231
|
-
</Layout>
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
232
|
### `useHydration()`
|
|
237
233
|
|
|
238
|
-
Track hydration state for client
|
|
234
|
+
Track hydration state for handling SSR vs client rendering differences.
|
|
239
235
|
|
|
240
236
|
```tsx
|
|
241
237
|
import { useHydration } from '@riktajs/react';
|
|
@@ -243,21 +239,14 @@ import { useHydration } from '@riktajs/react';
|
|
|
243
239
|
function TimeDisplay() {
|
|
244
240
|
const { isHydrated, isServer } = useHydration();
|
|
245
241
|
|
|
246
|
-
//
|
|
242
|
+
// On server and initial render, show static content
|
|
247
243
|
if (!isHydrated) {
|
|
248
244
|
return <span>Loading time...</span>;
|
|
249
245
|
}
|
|
250
246
|
|
|
247
|
+
// After hydration, show dynamic content
|
|
251
248
|
return <span>{new Date().toLocaleTimeString()}</span>;
|
|
252
249
|
}
|
|
253
|
-
|
|
254
|
-
function ClientOnlyComponent() {
|
|
255
|
-
const { isHydrated } = useHydration();
|
|
256
|
-
|
|
257
|
-
if (!isHydrated) return null;
|
|
258
|
-
|
|
259
|
-
return <SomeClientOnlyLibrary />;
|
|
260
|
-
}
|
|
261
250
|
```
|
|
262
251
|
|
|
263
252
|
### `useFetch()`
|
|
@@ -267,11 +256,6 @@ Data fetching hook with loading and error states.
|
|
|
267
256
|
```tsx
|
|
268
257
|
import { useFetch } from '@riktajs/react';
|
|
269
258
|
|
|
270
|
-
interface User {
|
|
271
|
-
id: string;
|
|
272
|
-
name: string;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
259
|
function UserProfile({ userId }: { userId: string }) {
|
|
276
260
|
const { data, loading, error, refetch } = useFetch<User>(
|
|
277
261
|
`/api/users/${userId}`
|
|
@@ -288,40 +272,20 @@ function UserProfile({ userId }: { userId: string }) {
|
|
|
288
272
|
</div>
|
|
289
273
|
);
|
|
290
274
|
}
|
|
291
|
-
|
|
292
|
-
// With options
|
|
293
|
-
const { data } = useFetch<Item[]>('/api/items', {
|
|
294
|
-
headers: { 'Authorization': `Bearer ${token}` },
|
|
295
|
-
deps: [token], // Refetch when token changes
|
|
296
|
-
skip: !token, // Don't fetch until we have a token
|
|
297
|
-
transform: (res) => res.results, // Transform response
|
|
298
|
-
});
|
|
299
275
|
```
|
|
300
276
|
|
|
301
277
|
### `useAction()`
|
|
302
278
|
|
|
303
|
-
Execute server actions (form submissions
|
|
279
|
+
Execute server actions (mutations, form submissions).
|
|
304
280
|
|
|
305
281
|
```tsx
|
|
306
282
|
import { useAction } from '@riktajs/react';
|
|
307
283
|
|
|
308
|
-
interface CreateItemInput {
|
|
309
|
-
name: string;
|
|
310
|
-
price: number;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
interface Item {
|
|
314
|
-
id: string;
|
|
315
|
-
name: string;
|
|
316
|
-
price: number;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
284
|
function CreateItemForm() {
|
|
320
285
|
const { execute, pending, result } = useAction<CreateItemInput, Item>(
|
|
321
286
|
'/api/items',
|
|
322
287
|
{
|
|
323
288
|
onSuccess: (item) => console.log('Created:', item),
|
|
324
|
-
onError: (error) => console.error('Failed:', error),
|
|
325
289
|
}
|
|
326
290
|
);
|
|
327
291
|
|
|
@@ -345,54 +309,64 @@ function CreateItemForm() {
|
|
|
345
309
|
</form>
|
|
346
310
|
);
|
|
347
311
|
}
|
|
348
|
-
|
|
349
|
-
// DELETE action
|
|
350
|
-
const { execute, pending } = useAction<{ id: string }, void>(
|
|
351
|
-
'/api/items',
|
|
352
|
-
{ method: 'DELETE' }
|
|
353
|
-
);
|
|
354
312
|
```
|
|
355
313
|
|
|
356
|
-
## TypeScript
|
|
314
|
+
## TypeScript Support
|
|
357
315
|
|
|
358
|
-
All
|
|
316
|
+
All hooks and components are fully typed. Export types are available:
|
|
359
317
|
|
|
360
318
|
```tsx
|
|
361
319
|
import type {
|
|
362
320
|
SsrData,
|
|
363
|
-
RouterContextValue,
|
|
364
|
-
NavigateOptions,
|
|
365
321
|
ActionResult,
|
|
366
322
|
FetchState,
|
|
367
323
|
ActionState,
|
|
368
|
-
|
|
324
|
+
RiktaProviderProps,
|
|
369
325
|
HydrationState,
|
|
370
326
|
Location,
|
|
327
|
+
NavigateFn,
|
|
328
|
+
NavigateOptions,
|
|
371
329
|
} from '@riktajs/react';
|
|
372
330
|
```
|
|
373
331
|
|
|
374
|
-
##
|
|
332
|
+
## Migration from Previous Versions
|
|
375
333
|
|
|
376
|
-
|
|
334
|
+
If you were using the `<Link>` component or `useNavigation()`:
|
|
377
335
|
|
|
336
|
+
### Before (v1.x)
|
|
378
337
|
```tsx
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
338
|
+
import { Link, useNavigation } from '@riktajs/react';
|
|
339
|
+
|
|
340
|
+
function Nav() {
|
|
341
|
+
const { navigate, pathname } = useNavigation();
|
|
342
|
+
|
|
343
|
+
return (
|
|
344
|
+
<nav>
|
|
345
|
+
<Link href="/about">About</Link>
|
|
346
|
+
<button onClick={() => navigate('/search', { state: { from: 'nav' } })}>
|
|
347
|
+
Search
|
|
348
|
+
</button>
|
|
349
|
+
</nav>
|
|
350
|
+
);
|
|
388
351
|
}
|
|
352
|
+
```
|
|
389
353
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
354
|
+
### After (v2.x)
|
|
355
|
+
```tsx
|
|
356
|
+
import { useNavigate, useLocation } from '@riktajs/react';
|
|
357
|
+
|
|
358
|
+
function Nav() {
|
|
359
|
+
const navigate = useNavigate();
|
|
360
|
+
const { pathname } = useLocation();
|
|
394
361
|
|
|
395
|
-
return
|
|
362
|
+
return (
|
|
363
|
+
<nav>
|
|
364
|
+
<a href="/about">About</a>
|
|
365
|
+
<button onClick={() => navigate('/search', { q: '' })}>
|
|
366
|
+
Search
|
|
367
|
+
</button>
|
|
368
|
+
</nav>
|
|
369
|
+
);
|
|
396
370
|
}
|
|
397
371
|
```
|
|
398
372
|
|