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 CHANGED
@@ -1,75 +1,80 @@
1
1
  # react-solidlike
2
2
 
3
- [English](./README.en.md) | 中文
3
+ English | [中文](./README.zh.md)
4
4
 
5
- 声明式 React 控制流组件库,灵感来源于 Solid.js。用于替代 JSX 中的三元表达式和 `array.map()`,让你的组件代码更加清晰易读。支持 React React Native
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
- // fallback
29
+ // With fallback
30
30
  <Show when={isLoggedIn} fallback={<LoginButton />}>
31
31
  <UserProfile />
32
32
  </Show>
33
33
 
34
- // 使用 render props 获取类型安全的值
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
- 替代 `array.map()` 进行列表渲染。
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
- // keyExtractor
57
+ // With keyExtractor
53
58
  <For each={users} keyExtractor={(user) => user.id}>
54
59
  {(user) => <UserCard user={user} />}
55
60
  </For>
56
61
 
57
- // fallback 处理空数组
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
- // 使用 wrapper 包装元素
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
- // 使用 array 参数获取上下文信息
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
- 替代多个 `if-else` `switch` 语句。
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
- 等待 Promise resolve 后渲染内容。
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
- // 支持非 Promise 值(用于缓存场景)
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
- 替代 `Array.from({ length: n }).map()`。
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
- // 使用 wrapper 包装元素
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}>倒序 {i}</div>}
163
+ {(i) => <div key={i}>Reversed {i}</div>}
159
164
  </Repeat>
160
165
 
161
- // 使用 length 参数显示进度
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
- // 渲染: ["a", "b", "c"]
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
- // 渲染: ["9+5", "=", "(9+1)+4"]
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
- // 渲染: ["a", "1", "b", "2", "c", "3"]
197
+ // Renders: ["a", "1", "b", "2", "c", "3"]
193
198
 
194
- // wrapper 包装元素
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
- // fallback 处理空字符串
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
- // 渲染顺序: ["c", "b", "a"]
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
- 捕获子组件树中的 JavaScript 错误,防止整个应用崩溃。
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
- // 使用 render props 获取错误信息和重置函数
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
- 处理异步查询的各种状态(加载中、错误、空数据、成功)。可与 `@tanstack/react-query`、SWRRTK Query 等配合使用。
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` | `QueryResult<T>` | 查询结果对象 |
307
- | `loading` | `ReactNode` | 加载中显示 |
308
- | `error` | `ReactNode` | 错误时显示 |
309
- | `empty` | `ReactNode` | 空数据显示 |
310
- | `children` | `ReactNode \| (data: T) => ReactNode` | 成功时渲染 |
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