react-solidlike 2.3.0 → 2.5.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.md +195 -70
- package/README.zh.md +456 -0
- package/dist/ClientOnly.d.ts +43 -0
- package/dist/Once.d.ts +43 -0
- package/dist/Repeat.d.ts +3 -3
- package/dist/Show.d.ts +9 -1
- package/dist/Split.d.ts +3 -3
- package/dist/Timeout.d.ts +45 -0
- package/dist/Visible.d.ts +51 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +71 -4
- package/package.json +5 -2
- package/README.en.md +0 -331
package/README.md
CHANGED
|
@@ -1,75 +1,80 @@
|
|
|
1
1
|
# react-solidlike
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+
English | [中文](./README.zh.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Declarative React control flow components inspired by Solid.js. Replaces ternary expressions and `array.map()` in JSX, making your component code cleaner and more readable. Supports React and React Native.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npm install react-solidlike
|
|
11
|
-
#
|
|
11
|
+
# or
|
|
12
12
|
bun add react-solidlike
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Components
|
|
16
16
|
|
|
17
|
-
### `<Show>` -
|
|
17
|
+
### `<Show>` - Conditional Rendering
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Replace ternary expressions for conditional rendering.
|
|
20
20
|
|
|
21
21
|
```tsx
|
|
22
22
|
import { Show } from "react-solidlike";
|
|
23
23
|
|
|
24
|
-
//
|
|
24
|
+
// Basic usage
|
|
25
25
|
<Show when={isLoggedIn}>
|
|
26
26
|
<UserProfile />
|
|
27
27
|
</Show>
|
|
28
28
|
|
|
29
|
-
//
|
|
29
|
+
// With fallback
|
|
30
30
|
<Show when={isLoggedIn} fallback={<LoginButton />}>
|
|
31
31
|
<UserProfile />
|
|
32
32
|
</Show>
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// Using render props for type-safe value access
|
|
35
35
|
<Show when={user}>
|
|
36
36
|
{(user) => <UserProfile name={user.name} />}
|
|
37
37
|
</Show>
|
|
38
|
+
|
|
39
|
+
// With onFallback callback (for redirects and other side effects)
|
|
40
|
+
<Show when={isAuthenticated} fallback={<Loading />} onFallback={() => navigate('/login')}>
|
|
41
|
+
<Dashboard />
|
|
42
|
+
</Show>
|
|
38
43
|
```
|
|
39
44
|
|
|
40
|
-
### `<For>` -
|
|
45
|
+
### `<For>` - List Rendering
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
Replace `array.map()` for list rendering.
|
|
43
48
|
|
|
44
49
|
```tsx
|
|
45
50
|
import { For } from "react-solidlike";
|
|
46
51
|
|
|
47
|
-
//
|
|
52
|
+
// Basic usage
|
|
48
53
|
<For each={items}>
|
|
49
54
|
{(item) => <ListItem {...item} />}
|
|
50
55
|
</For>
|
|
51
56
|
|
|
52
|
-
//
|
|
57
|
+
// With keyExtractor
|
|
53
58
|
<For each={users} keyExtractor={(user) => user.id}>
|
|
54
59
|
{(user) => <UserCard user={user} />}
|
|
55
60
|
</For>
|
|
56
61
|
|
|
57
|
-
//
|
|
62
|
+
// With fallback for empty arrays
|
|
58
63
|
<For each={items} fallback={<EmptyState />}>
|
|
59
64
|
{(item, index) => <ListItem item={item} index={index} />}
|
|
60
65
|
</For>
|
|
61
66
|
|
|
62
|
-
//
|
|
67
|
+
// With wrapper element
|
|
63
68
|
<For each={items} wrapper={<ul className="list" />}>
|
|
64
69
|
{(item) => <li>{item.name}</li>}
|
|
65
70
|
</For>
|
|
66
71
|
|
|
67
|
-
//
|
|
72
|
+
// Reverse rendering
|
|
68
73
|
<For each={messages} reverse>
|
|
69
74
|
{(msg) => <Message {...msg} />}
|
|
70
75
|
</For>
|
|
71
76
|
|
|
72
|
-
//
|
|
77
|
+
// Using array parameter for context
|
|
73
78
|
<For each={steps}>
|
|
74
79
|
{(step, index, array) => (
|
|
75
80
|
<Step
|
|
@@ -81,9 +86,9 @@ import { For } from "react-solidlike";
|
|
|
81
86
|
</For>
|
|
82
87
|
```
|
|
83
88
|
|
|
84
|
-
### `<Switch>` / `<Match>` / `<Default>` -
|
|
89
|
+
### `<Switch>` / `<Match>` / `<Default>` - Multi-branch Rendering
|
|
85
90
|
|
|
86
|
-
|
|
91
|
+
Replace multiple `if-else` or `switch` statements.
|
|
87
92
|
|
|
88
93
|
```tsx
|
|
89
94
|
import { Switch, Match, Default } from "react-solidlike";
|
|
@@ -104,19 +109,19 @@ import { Switch, Match, Default } from "react-solidlike";
|
|
|
104
109
|
</Switch>
|
|
105
110
|
```
|
|
106
111
|
|
|
107
|
-
### `<Await>` -
|
|
112
|
+
### `<Await>` - Async Rendering
|
|
108
113
|
|
|
109
|
-
|
|
114
|
+
Wait for Promise to resolve before rendering.
|
|
110
115
|
|
|
111
116
|
```tsx
|
|
112
117
|
import { Await } from "react-solidlike";
|
|
113
118
|
|
|
114
|
-
//
|
|
119
|
+
// Basic usage
|
|
115
120
|
<Await promise={fetchUser()} loading={<Spinner />}>
|
|
116
121
|
{(user) => <UserProfile user={user} />}
|
|
117
122
|
</Await>
|
|
118
123
|
|
|
119
|
-
//
|
|
124
|
+
// With error handling
|
|
120
125
|
<Await
|
|
121
126
|
promise={fetchData()}
|
|
122
127
|
loading={<Loading />}
|
|
@@ -125,40 +130,40 @@ import { Await } from "react-solidlike";
|
|
|
125
130
|
{(data) => <DataView data={data} />}
|
|
126
131
|
</Await>
|
|
127
132
|
|
|
128
|
-
//
|
|
133
|
+
// Supports non-Promise values (for caching scenarios)
|
|
129
134
|
<Await promise={cache ?? fetchData()} loading={<Spinner />}>
|
|
130
135
|
{(data) => <DataView data={data} />}
|
|
131
136
|
</Await>
|
|
132
137
|
```
|
|
133
138
|
|
|
134
|
-
### `<Repeat>` -
|
|
139
|
+
### `<Repeat>` - Repeat Rendering
|
|
135
140
|
|
|
136
|
-
|
|
141
|
+
Replace `Array.from({ length: n }).map()`.
|
|
137
142
|
|
|
138
143
|
```tsx
|
|
139
144
|
import { Repeat } from "react-solidlike";
|
|
140
145
|
|
|
141
|
-
//
|
|
146
|
+
// Render star ratings
|
|
142
147
|
<Repeat times={5}>
|
|
143
148
|
{(i) => <Star key={i} filled={i < rating} />}
|
|
144
149
|
</Repeat>
|
|
145
150
|
|
|
146
|
-
//
|
|
151
|
+
// Generate skeleton placeholders
|
|
147
152
|
<Repeat times={3}>
|
|
148
153
|
{(i) => <SkeletonCard key={i} />}
|
|
149
154
|
</Repeat>
|
|
150
155
|
|
|
151
|
-
//
|
|
156
|
+
// With wrapper element
|
|
152
157
|
<Repeat times={5} wrapper={<div className="stars" />}>
|
|
153
158
|
{(i) => <Star key={i} />}
|
|
154
159
|
</Repeat>
|
|
155
160
|
|
|
156
|
-
//
|
|
161
|
+
// Reverse rendering
|
|
157
162
|
<Repeat times={5} reverse>
|
|
158
|
-
{(i) => <div key={i}
|
|
163
|
+
{(i) => <div key={i}>Reversed {i}</div>}
|
|
159
164
|
</Repeat>
|
|
160
165
|
|
|
161
|
-
//
|
|
166
|
+
// Using length parameter for progress
|
|
162
167
|
<Repeat times={totalSteps}>
|
|
163
168
|
{(i, length) => (
|
|
164
169
|
<Step key={i} current={i + 1} total={length} />
|
|
@@ -166,56 +171,56 @@ import { Repeat } from "react-solidlike";
|
|
|
166
171
|
</Repeat>
|
|
167
172
|
```
|
|
168
173
|
|
|
169
|
-
### `<Split>` -
|
|
174
|
+
### `<Split>` - String Split Rendering
|
|
170
175
|
|
|
171
|
-
|
|
176
|
+
Split a string by separator and render each part.
|
|
172
177
|
|
|
173
178
|
```tsx
|
|
174
179
|
import { Split } from "react-solidlike";
|
|
175
180
|
|
|
176
|
-
//
|
|
181
|
+
// Basic usage - splits without keeping separator
|
|
177
182
|
<Split string="a,b,c" separator=",">
|
|
178
183
|
{(part) => <span>{part}</span>}
|
|
179
184
|
</Split>
|
|
180
|
-
//
|
|
185
|
+
// Renders: ["a", "b", "c"]
|
|
181
186
|
|
|
182
|
-
//
|
|
187
|
+
// Keep separator in result
|
|
183
188
|
<Split string="9+5=(9+1)+4" separator="=" keepSeparator>
|
|
184
189
|
{(part) => <span>{part}</span>}
|
|
185
190
|
</Split>
|
|
186
|
-
//
|
|
191
|
+
// Renders: ["9+5", "=", "(9+1)+4"]
|
|
187
192
|
|
|
188
|
-
//
|
|
193
|
+
// Using RegExp separator
|
|
189
194
|
<Split string="a1b2c3" separator={/\d/} keepSeparator>
|
|
190
195
|
{(part) => <span>{part}</span>}
|
|
191
196
|
</Split>
|
|
192
|
-
//
|
|
197
|
+
// Renders: ["a", "1", "b", "2", "c", "3"]
|
|
193
198
|
|
|
194
|
-
//
|
|
199
|
+
// With wrapper element
|
|
195
200
|
<Split string="hello world" separator=" " wrapper={<div className="words" />}>
|
|
196
201
|
{(word) => <span>{word}</span>}
|
|
197
202
|
</Split>
|
|
198
203
|
|
|
199
|
-
//
|
|
204
|
+
// With fallback for empty string
|
|
200
205
|
<Split string={text} separator="," fallback={<EmptyState />}>
|
|
201
206
|
{(part) => <Tag>{part}</Tag>}
|
|
202
207
|
</Split>
|
|
203
208
|
|
|
204
|
-
//
|
|
209
|
+
// Reverse rendering
|
|
205
210
|
<Split string="a,b,c" separator="," reverse>
|
|
206
211
|
{(part) => <span>{part}</span>}
|
|
207
212
|
</Split>
|
|
208
|
-
//
|
|
213
|
+
// Render order: ["c", "b", "a"]
|
|
209
214
|
```
|
|
210
215
|
|
|
211
|
-
### `<Dynamic>` -
|
|
216
|
+
### `<Dynamic>` - Dynamic Component
|
|
212
217
|
|
|
213
|
-
|
|
218
|
+
Dynamically select component type based on conditions.
|
|
214
219
|
|
|
215
220
|
```tsx
|
|
216
221
|
import { Dynamic } from "react-solidlike";
|
|
217
222
|
|
|
218
|
-
//
|
|
223
|
+
// Dynamic button or link
|
|
219
224
|
<Dynamic
|
|
220
225
|
component={href ? 'a' : 'button'}
|
|
221
226
|
href={href}
|
|
@@ -224,13 +229,13 @@ import { Dynamic } from "react-solidlike";
|
|
|
224
229
|
{label}
|
|
225
230
|
</Dynamic>
|
|
226
231
|
|
|
227
|
-
//
|
|
232
|
+
// With custom components
|
|
228
233
|
<Dynamic
|
|
229
234
|
component={isAdmin ? AdminPanel : UserPanel}
|
|
230
235
|
user={currentUser}
|
|
231
236
|
/>
|
|
232
237
|
|
|
233
|
-
// React Native
|
|
238
|
+
// React Native usage
|
|
234
239
|
<Dynamic
|
|
235
240
|
component={isPressable ? Pressable : View}
|
|
236
241
|
onPress={handlePress}
|
|
@@ -239,19 +244,19 @@ import { Dynamic } from "react-solidlike";
|
|
|
239
244
|
</Dynamic>
|
|
240
245
|
```
|
|
241
246
|
|
|
242
|
-
### `<ErrorBoundary>` -
|
|
247
|
+
### `<ErrorBoundary>` - Error Boundary
|
|
243
248
|
|
|
244
|
-
|
|
249
|
+
Catch JavaScript errors in child component tree.
|
|
245
250
|
|
|
246
251
|
```tsx
|
|
247
252
|
import { ErrorBoundary } from "react-solidlike";
|
|
248
253
|
|
|
249
|
-
//
|
|
254
|
+
// Basic usage
|
|
250
255
|
<ErrorBoundary fallback={<ErrorPage />}>
|
|
251
256
|
<App />
|
|
252
257
|
</ErrorBoundary>
|
|
253
258
|
|
|
254
|
-
//
|
|
259
|
+
// With render props for error info and reset function
|
|
255
260
|
<ErrorBoundary
|
|
256
261
|
fallback={(error, reset) => (
|
|
257
262
|
<div>
|
|
@@ -263,15 +268,15 @@ import { ErrorBoundary } from "react-solidlike";
|
|
|
263
268
|
<App />
|
|
264
269
|
</ErrorBoundary>
|
|
265
270
|
|
|
266
|
-
// resetKey
|
|
271
|
+
// Auto-reset when resetKey changes
|
|
267
272
|
<ErrorBoundary fallback={<Error />} resetKey={userId}>
|
|
268
273
|
<UserProfile />
|
|
269
274
|
</ErrorBoundary>
|
|
270
275
|
```
|
|
271
276
|
|
|
272
|
-
### `<QueryBoundary>` -
|
|
277
|
+
### `<QueryBoundary>` - Query Boundary
|
|
273
278
|
|
|
274
|
-
|
|
279
|
+
Handle async query states (loading, error, empty, success). Works with `@tanstack/react-query`, SWR, RTK Query, etc.
|
|
275
280
|
|
|
276
281
|
```tsx
|
|
277
282
|
import { QueryBoundary } from "react-solidlike";
|
|
@@ -301,28 +306,148 @@ function UserList() {
|
|
|
301
306
|
|
|
302
307
|
#### Props
|
|
303
308
|
|
|
304
|
-
|
|
|
305
|
-
|
|
306
|
-
| `query`
|
|
307
|
-
| `loading`
|
|
308
|
-
| `error`
|
|
309
|
-
| `empty`
|
|
310
|
-
| `children`
|
|
311
|
-
| `isEmptyFn` | `(data: T) => boolean`
|
|
309
|
+
| Prop | Type | Description |
|
|
310
|
+
| ----------- | ------------------------------------- | --------------------- |
|
|
311
|
+
| `query` | `QueryResult<T>` | Query result object |
|
|
312
|
+
| `loading` | `ReactNode` | Loading state content |
|
|
313
|
+
| `error` | `ReactNode` | Error state content |
|
|
314
|
+
| `empty` | `ReactNode` | Empty state content |
|
|
315
|
+
| `children` | `ReactNode \| (data: T) => ReactNode` | Success content |
|
|
316
|
+
| `isEmptyFn` | `(data: T) => boolean` | Custom empty check |
|
|
317
|
+
|
|
318
|
+
### `<Once>` - Single Render
|
|
319
|
+
|
|
320
|
+
Renders children only once and ignores subsequent updates. Useful for expensive computations or content that shouldn't re-render.
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
import { Once } from "react-solidlike";
|
|
324
|
+
|
|
325
|
+
// Render expensive component once
|
|
326
|
+
<Once>
|
|
327
|
+
<ExpensiveChart data={data} />
|
|
328
|
+
</Once>
|
|
329
|
+
|
|
330
|
+
// Prevent re-renders from parent updates
|
|
331
|
+
function Parent() {
|
|
332
|
+
const [count, setCount] = useState(0);
|
|
333
|
+
return (
|
|
334
|
+
<div>
|
|
335
|
+
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
|
|
336
|
+
<Once>
|
|
337
|
+
<Child initialCount={count} />
|
|
338
|
+
</Once>
|
|
339
|
+
</div>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### `<ClientOnly>` - Client-side Only Rendering
|
|
345
|
+
|
|
346
|
+
Renders children only on the client side (after hydration). Useful for components that rely on browser APIs or need to avoid SSR hydration mismatches.
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
import { ClientOnly } from "react-solidlike";
|
|
350
|
+
|
|
351
|
+
// Basic usage
|
|
352
|
+
<ClientOnly>
|
|
353
|
+
<BrowserOnlyComponent />
|
|
354
|
+
</ClientOnly>
|
|
355
|
+
|
|
356
|
+
// With SSR fallback
|
|
357
|
+
<ClientOnly fallback={<Skeleton />}>
|
|
358
|
+
<DynamicChart />
|
|
359
|
+
</ClientOnly>
|
|
360
|
+
|
|
361
|
+
// Using render function for lazy evaluation (avoid accessing window)
|
|
362
|
+
<ClientOnly fallback={<Loading />}>
|
|
363
|
+
{() => <ComponentUsingWindow width={window.innerWidth} />}
|
|
364
|
+
</ClientOnly>
|
|
365
|
+
|
|
366
|
+
// Avoid hydration mismatch
|
|
367
|
+
<ClientOnly fallback={<span>--:--</span>}>
|
|
368
|
+
<CurrentTime />
|
|
369
|
+
</ClientOnly>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### `<Timeout>` - Timeout Rendering
|
|
373
|
+
|
|
374
|
+
Shows or hides content after a specified delay. Useful for auto-dismissing notifications, delayed loading scenarios.
|
|
375
|
+
|
|
376
|
+
```tsx
|
|
377
|
+
import { Timeout } from "react-solidlike";
|
|
378
|
+
|
|
379
|
+
// Show after delay (mode="after", default)
|
|
380
|
+
<Timeout ms={1000} mode="after" fallback={<Spinner />}>
|
|
381
|
+
<DelayedContent />
|
|
382
|
+
</Timeout>
|
|
383
|
+
|
|
384
|
+
// Hide after delay (mode="before")
|
|
385
|
+
<Timeout ms={3000} mode="before">
|
|
386
|
+
<Toast message="Success!" />
|
|
387
|
+
</Timeout>
|
|
388
|
+
|
|
389
|
+
// Auto-dismiss notification
|
|
390
|
+
<Timeout ms={5000} mode="before" onTimeout={() => console.log("dismissed")}>
|
|
391
|
+
<Notification type="success">Saved successfully</Notification>
|
|
392
|
+
</Timeout>
|
|
393
|
+
|
|
394
|
+
// Delayed render with loading state
|
|
395
|
+
<Timeout ms={2000} mode="after" fallback={<Skeleton />}>
|
|
396
|
+
<ExpensiveComponent />
|
|
397
|
+
</Timeout>
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
#### Props
|
|
401
|
+
|
|
402
|
+
| Prop | Type | Description |
|
|
403
|
+
| ----------- | --------------------- | ------------------------------------------------------------------------------- |
|
|
404
|
+
| `ms` | `number` | Delay time in milliseconds |
|
|
405
|
+
| `mode` | `'after' \| 'before'` | `'after'` = show after delay, `'before'` = hide after delay (default `'after'`) |
|
|
406
|
+
| `children` | `ReactNode` | Content to render |
|
|
407
|
+
| `fallback` | `ReactNode` | Content to show while waiting (`after` mode only) |
|
|
408
|
+
| `onTimeout` | `() => void` | Callback when timeout occurs |
|
|
409
|
+
|
|
410
|
+
### `<Visible>` - Visibility-based Rendering (Web only)
|
|
411
|
+
|
|
412
|
+
Renders children when entering viewport using IntersectionObserver. In React Native or unsupported environments, children are rendered directly (graceful degradation).
|
|
413
|
+
|
|
414
|
+
```tsx
|
|
415
|
+
import { Visible } from "react-solidlike";
|
|
416
|
+
|
|
417
|
+
// Basic usage - render when entering viewport
|
|
418
|
+
<Visible>
|
|
419
|
+
<HeavyComponent />
|
|
420
|
+
</Visible>
|
|
421
|
+
|
|
422
|
+
// With placeholder
|
|
423
|
+
<Visible fallback={<Skeleton />}>
|
|
424
|
+
<Image src={url} />
|
|
425
|
+
</Visible>
|
|
426
|
+
|
|
427
|
+
// Preload before entering viewport (rootMargin)
|
|
428
|
+
<Visible rootMargin="200px" fallback={<Placeholder />}>
|
|
429
|
+
<LazyImage />
|
|
430
|
+
</Visible>
|
|
431
|
+
|
|
432
|
+
// Toggle visibility (once=false unmounts when leaving viewport)
|
|
433
|
+
<Visible once={false} onVisibilityChange={(v) => console.log(v)}>
|
|
434
|
+
<VideoPlayer />
|
|
435
|
+
</Visible>
|
|
436
|
+
```
|
|
312
437
|
|
|
313
|
-
##
|
|
438
|
+
## Development
|
|
314
439
|
|
|
315
440
|
```bash
|
|
316
|
-
#
|
|
441
|
+
# Install dependencies
|
|
317
442
|
bun install
|
|
318
443
|
|
|
319
|
-
#
|
|
444
|
+
# Run tests
|
|
320
445
|
bun test
|
|
321
446
|
|
|
322
|
-
#
|
|
447
|
+
# Lint
|
|
323
448
|
bun run lint
|
|
324
449
|
|
|
325
|
-
#
|
|
450
|
+
# Build
|
|
326
451
|
bun run build
|
|
327
452
|
```
|
|
328
453
|
|