react-solidlike 2.5.2 → 2.5.4

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.
@@ -13,6 +13,8 @@ export interface QueryResult<T> {
13
13
  export interface QueryBoundaryProps<T> {
14
14
  /** Query result object, usually from @tanstack/react-query's useQuery | 查询结果对象,通常来自 @tanstack/react-query 的 useQuery */
15
15
  query: QueryResult<T> | null | undefined;
16
+ /** When false, renders nothing directly | 为 false 时直接不渲染任何内容 */
17
+ when?: boolean;
16
18
  /** Content to show while loading | 加载中时显示的内容 */
17
19
  loading?: ReactNode | (() => ReactNode);
18
20
  /** Content to show when error occurs | 发生错误时显示的内容 */
@@ -57,4 +59,4 @@ export interface QueryBoundaryProps<T> {
57
59
  * {(data) => <ItemList items={data.items} />}
58
60
  * </QueryBoundary>
59
61
  */
60
- export declare function QueryBoundary<T>({ query, loading, error, empty, children, isEmptyFn, }: QueryBoundaryProps<T>): ReactNode;
62
+ export declare function QueryBoundary<T>({ query, when, loading, error, empty, children, isEmptyFn, }: QueryBoundaryProps<T>): ReactNode;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export { Await, type AwaitProps } from "./Await";
2
+ export { ClientOnly, type ClientOnlyProps } from "./ClientOnly";
2
3
  export { Dynamic, type DynamicProps } from "./Dynamic";
3
4
  export { ErrorBoundary, type ErrorBoundaryProps } from "./ErrorBoundary";
4
5
  export { For, type ForProps } from "./For";
6
+ export { Once, type OnceProps } from "./Once";
5
7
  export { QueryBoundary, type QueryBoundaryProps, type QueryResult } from "./QueryBoundary";
6
8
  export { Repeat, type RepeatProps } from "./Repeat";
7
9
  export { Show, type ShowProps } from "./Show";
package/dist/index.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import { Children, Component, Fragment, cloneElement, createElement, isValidElement, useEffect, useRef, useState } from "react";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
 
4
+ function resolveNode(node) {
5
+ return typeof node === "function" ? node() : node;
6
+ }
7
+
4
8
  function Await({ promise, loading = null, error = null, children }) {
5
9
  const [state, setState] = useState(() => {
6
10
  if (!(promise instanceof Promise)) return {
@@ -34,17 +38,24 @@ function Await({ promise, loading = null, error = null, children }) {
34
38
  cancelled = true;
35
39
  };
36
40
  }, [promise]);
37
- if (state.status === "pending") return typeof loading === "function" ? loading() : loading;
38
- if (state.status === "rejected") {
39
- if (typeof error === "function") return error(state.error);
40
- return error;
41
- }
41
+ if (state.status === "pending") return resolveNode(loading);
42
+ if (state.status === "rejected") return typeof error === "function" ? error(state.error) : error;
42
43
  if (typeof children === "function") return children(state.value);
43
44
  return children;
44
45
  }
45
46
 
47
+ function ClientOnly({ children, fallback = null }) {
48
+ const [isClient, setIsClient] = useState(false);
49
+ useEffect(() => {
50
+ setIsClient(true);
51
+ }, []);
52
+ if (!isClient) return resolveNode(fallback);
53
+ if (typeof children === "function") return children();
54
+ return children;
55
+ }
56
+
46
57
  function Dynamic({ component, fallback = null, ...props }) {
47
- if (!component) return typeof fallback === "function" ? fallback() : fallback;
58
+ if (!component) return resolveNode(fallback);
48
59
  return createElement(component, props);
49
60
  }
50
61
 
@@ -88,6 +99,18 @@ function For({ each, children, keyExtractor, fallback = null, wrapper, reverse }
88
99
  return wrapper && isValidElement(wrapper) ? cloneElement(wrapper, {}, elements) : elements;
89
100
  }
90
101
 
102
+ function Once({ children }) {
103
+ const cachedRef = useRef({
104
+ rendered: false,
105
+ content: null
106
+ });
107
+ if (!cachedRef.current.rendered) {
108
+ cachedRef.current.rendered = true;
109
+ cachedRef.current.content = children;
110
+ }
111
+ return cachedRef.current.content;
112
+ }
113
+
91
114
  function defaultIsEmpty(data) {
92
115
  if (data == null) return true;
93
116
  if (Array.isArray(data)) return data.length === 0;
@@ -95,12 +118,12 @@ function defaultIsEmpty(data) {
95
118
  return false;
96
119
  }
97
120
 
98
- function QueryBoundary({ query, loading = null, error = null, empty = null, children, isEmptyFn = defaultIsEmpty }) {
99
- if (!query) return null;
121
+ function QueryBoundary({ query, when = true, loading = null, error = null, empty = null, children, isEmptyFn = defaultIsEmpty }) {
122
+ if (!when || !query) return null;
100
123
  const { data, isPending, isError, isEmpty: queryIsEmpty } = query;
101
- if (isPending) return typeof loading === "function" ? loading() : loading;
102
- if (isError && isEmptyFn(data)) return typeof error === "function" ? error() : error;
103
- if (queryIsEmpty ?? isEmptyFn(data)) return typeof empty === "function" ? empty() : empty;
124
+ if (isPending) return resolveNode(loading);
125
+ if (isError && isEmptyFn(data)) return resolveNode(error);
126
+ if (queryIsEmpty ?? isEmptyFn(data)) return resolveNode(empty);
104
127
  if (typeof children === "function") return children(data);
105
128
  return children;
106
129
  }
@@ -117,7 +140,7 @@ function Repeat({ times, children, ...props }) {
117
140
  function Show({ when, children, fallback = null, onFallback }) {
118
141
  if (!when || isEmpty(when)) {
119
142
  onFallback?.();
120
- return typeof fallback === "function" ? fallback() : fallback;
143
+ return resolveNode(fallback);
121
144
  }
122
145
  if (typeof children === "function") return children(when);
123
146
  return children;
@@ -169,7 +192,7 @@ function isDefaultElement(child) {
169
192
 
170
193
  function Switch({ children, fallback = null }) {
171
194
  const childArray = Children.toArray(children);
172
- let defaultContent = typeof fallback === "function" ? fallback() : fallback;
195
+ let defaultContent = resolveNode(fallback);
173
196
  for (const child of childArray) {
174
197
  if (isDefaultElement(child)) {
175
198
  defaultContent = child.props.children;
@@ -195,7 +218,7 @@ function Timeout({ ms, children, mode = "after", fallback = null, onTimeout }) {
195
218
  }, ms);
196
219
  return () => clearTimeout(timer);
197
220
  }, [ms, onTimeout]);
198
- if (mode === "after") return ready ? children : typeof fallback === "function" ? fallback() : fallback;
221
+ if (mode === "after") return ready ? children : resolveNode(fallback);
199
222
  return ready ? children : null;
200
223
  }
201
224
 
@@ -246,8 +269,8 @@ function Visible({ children, fallback = null, rootMargin = "0px", threshold = 0,
246
269
  if (!isSupported) return children;
247
270
  return /* @__PURE__ */ jsx("div", {
248
271
  ref,
249
- children: (once ? hasBeenVisible : isVisible) ? children : typeof fallback === "function" ? fallback() : fallback
272
+ children: (once ? hasBeenVisible : isVisible) ? children : resolveNode(fallback)
250
273
  });
251
274
  }
252
275
 
253
- export { Await, Default, Dynamic, ErrorBoundary, For, Match, QueryBoundary, Repeat, Show, Split, Switch, Timeout, Visible };
276
+ export { Await, ClientOnly, Default, Dynamic, ErrorBoundary, For, Match, Once, QueryBoundary, Repeat, Show, Split, Switch, Timeout, Visible };
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from "react";
2
+ /** Node that can be either a ReactNode or a function returning a ReactNode | 可以是 ReactNode 或返回 ReactNode 的函数 */
3
+ export type ResolvableNode = ReactNode | (() => ReactNode);
4
+ /** Resolve a node that may be a function into a ReactNode | 将可能是函数的节点解析为 ReactNode */
5
+ export declare function resolveNode(node: ResolvableNode): ReactNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-solidlike",
3
- "version": "2.5.2",
3
+ "version": "2.5.4",
4
4
  "description": "Declarative React control flow components inspired by Solid.js, replacing ternary expressions and array.map() in JSX",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,7 +53,7 @@
53
53
  "@types/react-dom": "^19.2.3",
54
54
  "react": "^19.2.3",
55
55
  "react-dom": "^19.2.3",
56
- "rolldown": "^1.0.0-rc.2",
56
+ "rolldown": "^1.0.0-rc.3",
57
57
  "typescript": "^5.9.3"
58
58
  }
59
59
  }