@faasjs/react 8.0.0-beta.2 → 8.0.0-beta.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.
package/README.md CHANGED
@@ -31,6 +31,7 @@ React plugin for FaasJS.
31
31
  - Fetch Data:
32
32
  - [faas](functions/faas.md): Fetch data from FaasJS.
33
33
  - [useFaas](functions/useFaas.md): Fetch data from FaasJS with hooks.
34
+ - [useFaasStream](functions/useFaasStream.md): Fetch streaming data from FaasJS with hooks.
34
35
  - [FaasDataWrapper](functions/FaasDataWrapper.md): Fetch data from FaasJS with a wrapper component.
35
36
  - [withFaasData](functions/withFaasData.md): Fetch data from FaasJS using a higher-order component (HOC).
36
37
 
@@ -49,12 +50,14 @@ npm install @faasjs/react react
49
50
  - [Form](functions/Form.md)
50
51
  - [FormItem](functions/FormItem.md)
51
52
  - [getClient](functions/getClient.md)
53
+ - [OptionalWrapper](functions/OptionalWrapper.md)
52
54
  - [useConstant](functions/useConstant.md)
53
55
  - [useEqualCallback](functions/useEqualCallback.md)
54
56
  - [useEqualEffect](functions/useEqualEffect.md)
55
57
  - [useEqualMemo](functions/useEqualMemo.md)
56
58
  - [useEqualMemoize](functions/useEqualMemoize.md)
57
59
  - [useFaas](functions/useFaas.md)
60
+ - [useFaasStream](functions/useFaasStream.md)
58
61
  - [usePrevious](functions/usePrevious.md)
59
62
  - [useSplittingState](functions/useSplittingState.md)
60
63
  - [useStateRef](functions/useStateRef.md)
@@ -71,10 +74,6 @@ npm install @faasjs/react react
71
74
  - [Response](interfaces/Response.md)
72
75
  - [ResponseError](interfaces/ResponseError.md)
73
76
 
74
- ## Namespaces
75
-
76
- - [OptionalWrapper](@faasjs/namespaces/OptionalWrapper/README.md)
77
-
78
77
  ## Type Aliases
79
78
 
80
79
  - [ErrorChildrenProps](type-aliases/ErrorChildrenProps.md)
@@ -105,6 +104,8 @@ npm install @faasjs/react react
105
104
  - [Options](type-aliases/Options.md)
106
105
  - [ResponseHeaders](type-aliases/ResponseHeaders.md)
107
106
  - [useFaasOptions](type-aliases/useFaasOptions.md)
107
+ - [UseFaasStreamOptions](type-aliases/UseFaasStreamOptions.md)
108
+ - [UseFaasStreamResult](type-aliases/UseFaasStreamResult.md)
108
109
 
109
110
  ## Variables
110
111
 
@@ -113,5 +114,4 @@ npm install @faasjs/react react
113
114
  - [FormDefaultElements](variables/FormDefaultElements.md)
114
115
  - [FormDefaultLang](variables/FormDefaultLang.md)
115
116
  - [FormDefaultRules](variables/FormDefaultRules.md)
116
- - [OptionalWrapper](variables/OptionalWrapper.md)
117
117
  - [useFormContext](variables/useFormContext.md)
package/dist/index.cjs CHANGED
@@ -107,8 +107,7 @@ var FaasDataWrapper = fixedForwardRef(
107
107
  }
108
108
  );
109
109
  Object.assign(FaasDataWrapper, {
110
- displayName: "FaasDataWrapper",
111
- whyDidYouRender: true
110
+ displayName: "FaasDataWrapper"
112
111
  });
113
112
  function withFaasData(Component2, faasProps) {
114
113
  return (props) => /* @__PURE__ */ jsxRuntime.jsx(FaasDataWrapper, { ...faasProps, children: /* @__PURE__ */ jsxRuntime.jsx(Component2, { ...props }) });
@@ -267,10 +266,8 @@ function useConstant(fn) {
267
266
  }
268
267
  return ref.current.v;
269
268
  }
270
- useConstant.whyDidYouRender = true;
271
269
  var ErrorBoundary = class extends react.Component {
272
270
  static displayName = "ErrorBoundary";
273
- static whyDidYouRender = true;
274
271
  constructor(props) {
275
272
  super(props);
276
273
  this.state = {
@@ -349,7 +346,6 @@ function createSplittingContext(defaultValue) {
349
346
  return children;
350
347
  }
351
348
  Provider.displayName = "SplittingContextProvider";
352
- Provider.whyDidYouRender = true;
353
349
  function use() {
354
350
  return useConstant(() => {
355
351
  const obj = /* @__PURE__ */ Object.create(null);
@@ -366,7 +362,6 @@ function createSplittingContext(defaultValue) {
366
362
  return Object.freeze(obj);
367
363
  });
368
364
  }
369
- use.whyDidYouRender = true;
370
365
  return {
371
366
  Provider,
372
367
  use
@@ -435,20 +430,17 @@ function FormInput({
435
430
  );
436
431
  }
437
432
  FormInput.displayName = "FormInput";
438
- FormInput.whyDidYouRender = true;
439
433
  function FormItem(props) {
440
434
  const { Elements, errors } = useFormContext();
441
435
  const Label = props.label?.Label ?? Elements.Label;
442
436
  return /* @__PURE__ */ jsxRuntime.jsx(Label, { name: props.name, ...props.label, error: errors[props.name], children: /* @__PURE__ */ jsxRuntime.jsx(FormInput, { name: props.name, rules: props.rules, ...props.input }) });
443
437
  }
444
438
  FormItem.displayName = "FormItem";
445
- FormItem.whyDidYouRender = true;
446
439
  function FormBody() {
447
440
  const { items } = useFormContext();
448
441
  return items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(FormItem, { ...item }, item.name));
449
442
  }
450
443
  FormBody.displayName = "FormBody";
451
- FormBody.whyDidYouRender = true;
452
444
  var FormButtonElement = react.forwardRef(({ children, submit, submitting, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
453
445
  "button",
454
446
  {
@@ -461,10 +453,8 @@ var FormButtonElement = react.forwardRef(({ children, submit, submitting, ...pro
461
453
  }
462
454
  ));
463
455
  FormButtonElement.displayName = "FormButtonElement";
464
- Object.assign(FormButtonElement, { whyDidYouRender: true });
465
456
  var FormInputElement = react.forwardRef(({ onChange, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("input", { ...props, onChange: (e) => onChange(e.target.value), ref }));
466
457
  FormInputElement.displayName = "FormInputElement";
467
- Object.assign(FormInputElement, { whyDidYouRender: true });
468
458
  var FormLabelElement = ({
469
459
  name,
470
460
  title,
@@ -480,7 +470,6 @@ var FormLabelElement = ({
480
470
  ] });
481
471
  };
482
472
  FormLabelElement.displayName = "FormLabelElement";
483
- FormLabelElement.whyDidYouRender = true;
484
473
 
485
474
  // src/Form/elements/index.ts
486
475
  var FormDefaultElements = {
@@ -558,7 +547,6 @@ function FormFooter() {
558
547
  return MemoizedButton;
559
548
  }
560
549
  FormFooter.displayName = "FormFooter";
561
- FormFooter.whyDidYouRender = true;
562
550
 
563
551
  // src/Form/lang.ts
564
552
  var FormDefaultLang = {
@@ -610,12 +598,140 @@ function FormContainer({
610
598
  );
611
599
  }
612
600
  FormContainer.displayName = "FormContainer";
613
- FormContainer.whyDidYouRender = true;
614
- var OptionalWrapper = ({ condition, Wrapper, wrapperProps, children }) => {
615
- return condition ? /* @__PURE__ */ jsxRuntime.jsx(Wrapper, { ...wrapperProps, children }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
616
- };
601
+ function OptionalWrapper({
602
+ condition,
603
+ Wrapper,
604
+ wrapperProps,
605
+ children
606
+ }) {
607
+ if (condition) return /* @__PURE__ */ jsxRuntime.jsx(Wrapper, { ...wrapperProps, children });
608
+ return children;
609
+ }
617
610
  OptionalWrapper.displayName = "OptionalWrapper";
618
- OptionalWrapper.whyDidYouRender = true;
611
+ function useFaasStream(action, defaultParams, options = {}) {
612
+ const [loading, setLoading] = react.useState(true);
613
+ const [data, setData] = react.useState(options.data || "");
614
+ const [error, setError] = react.useState();
615
+ const [params, setParams] = react.useState(defaultParams);
616
+ const [reloadTimes, setReloadTimes] = react.useState(0);
617
+ const [fails, setFails] = react.useState(0);
618
+ const [skip, setSkip] = react.useState(
619
+ typeof options.skip === "function" ? options.skip(defaultParams) : options.skip
620
+ );
621
+ const controllerRef = react.useRef(null);
622
+ const pendingReloadsRef = react.useRef(/* @__PURE__ */ new Map());
623
+ const reloadCounterRef = react.useRef(0);
624
+ useEqualEffect(() => {
625
+ setSkip(
626
+ typeof options.skip === "function" ? options.skip(params) : options.skip
627
+ );
628
+ }, [typeof options.skip === "function" ? params : options.skip]);
629
+ useEqualEffect(() => {
630
+ if (!equal(defaultParams, params)) {
631
+ setParams(defaultParams);
632
+ }
633
+ }, [defaultParams]);
634
+ useEqualEffect(() => {
635
+ if (!action || skip) {
636
+ setLoading(false);
637
+ return;
638
+ }
639
+ setLoading(true);
640
+ setData("");
641
+ controllerRef.current = new AbortController();
642
+ const client = getClient(options.baseUrl);
643
+ function send() {
644
+ client.browserClient.action(action, options.params || params, {
645
+ signal: controllerRef.current.signal,
646
+ stream: true
647
+ }).then(async (response) => {
648
+ if (!response.body) {
649
+ setError(new Error("Response body is null"));
650
+ setLoading(false);
651
+ return;
652
+ }
653
+ const reader = response.body.getReader();
654
+ const decoder = new TextDecoder();
655
+ let accumulatedText = "";
656
+ try {
657
+ while (true) {
658
+ const { done, value } = await reader.read();
659
+ if (done) break;
660
+ accumulatedText += decoder.decode(value, { stream: true });
661
+ setData(accumulatedText);
662
+ }
663
+ setFails(0);
664
+ setError(null);
665
+ setLoading(false);
666
+ for (const { resolve } of pendingReloadsRef.current.values())
667
+ resolve(accumulatedText);
668
+ pendingReloadsRef.current.clear();
669
+ } catch (readError) {
670
+ reader.releaseLock();
671
+ throw readError;
672
+ }
673
+ }).catch(async (e) => {
674
+ if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0)
675
+ return;
676
+ if (!fails && typeof e?.message === "string" && e.message.indexOf("Failed to fetch") >= 0) {
677
+ console.warn(`FaasReactClient: ${e.message} retry...`);
678
+ setFails(1);
679
+ return send();
680
+ }
681
+ let error2 = e;
682
+ if (client.onError)
683
+ try {
684
+ await client.onError(action, params)(e);
685
+ } catch (newError) {
686
+ error2 = newError;
687
+ }
688
+ setError(error2);
689
+ setLoading(false);
690
+ for (const { reject } of pendingReloadsRef.current.values())
691
+ reject(error2);
692
+ pendingReloadsRef.current.clear();
693
+ return;
694
+ });
695
+ }
696
+ if (options.debounce) {
697
+ const timeout = setTimeout(send, options.debounce);
698
+ return () => {
699
+ clearTimeout(timeout);
700
+ controllerRef.current?.abort();
701
+ setLoading(false);
702
+ };
703
+ }
704
+ send();
705
+ return () => {
706
+ controllerRef.current?.abort();
707
+ setLoading(false);
708
+ };
709
+ }, [action, options.params || params, reloadTimes, skip]);
710
+ const reload = useEqualCallback(
711
+ (params2) => {
712
+ if (skip) setSkip(false);
713
+ if (params2) setParams(params2);
714
+ const reloadCounter = ++reloadCounterRef.current;
715
+ return new Promise((resolve, reject) => {
716
+ pendingReloadsRef.current.set(reloadCounter, { resolve, reject });
717
+ setReloadTimes((prev) => prev + 1);
718
+ });
719
+ },
720
+ [params, skip]
721
+ );
722
+ return {
723
+ action,
724
+ params,
725
+ loading,
726
+ data: options.data || data,
727
+ reloadTimes,
728
+ error,
729
+ reload,
730
+ setData: options.setData || setData,
731
+ setLoading,
732
+ setError
733
+ };
734
+ }
619
735
  function usePrevious(value) {
620
736
  const ref = react.useRef(void 0);
621
737
  react.useEffect(() => {
@@ -644,6 +760,7 @@ exports.useEqualEffect = useEqualEffect;
644
760
  exports.useEqualMemo = useEqualMemo;
645
761
  exports.useEqualMemoize = useEqualMemoize;
646
762
  exports.useFaas = useFaas;
763
+ exports.useFaasStream = useFaasStream;
647
764
  exports.useFormContext = useFormContext;
648
765
  exports.usePrevious = usePrevious;
649
766
  exports.useSplittingState = useSplittingState;
package/dist/index.d.ts CHANGED
@@ -158,9 +158,6 @@ declare function getClient(host?: string): FaasReactClientInstance;
158
158
  * Returns a constant value that is created by the given function.
159
159
  */
160
160
  declare function useConstant<T>(fn: () => T): T;
161
- declare namespace useConstant {
162
- var whyDidYouRender: boolean;
163
- }
164
161
 
165
162
  interface ErrorBoundaryProps {
166
163
  children?: ReactNode;
@@ -180,7 +177,6 @@ declare class ErrorBoundary extends Component<ErrorBoundaryProps, {
180
177
  };
181
178
  }> {
182
179
  static displayName: string;
183
- static whyDidYouRender: boolean;
184
180
  constructor(props: ErrorBoundaryProps);
185
181
  componentDidCatch(error: Error | null, info: any): void;
186
182
  render(): string | number | bigint | boolean | react_jsx_runtime.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode>>;
@@ -358,7 +354,6 @@ type FormItemProps<FormElements extends FormElementTypes = FormElementTypes, For
358
354
  declare function FormItem(props: FormItemProps): react_jsx_runtime.JSX.Element;
359
355
  declare namespace FormItem {
360
356
  var displayName: string;
361
- var whyDidYouRender: boolean;
362
357
  }
363
358
 
364
359
  type FormProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules> = {
@@ -402,7 +397,6 @@ type FormProps<Values extends Record<string, any> = Record<string, any>, FormEle
402
397
  declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules>({ defaultValues, Elements, rules, lang, items, ...props }: FormProps<Values, FormElements, Rules>): react_jsx_runtime.JSX.Element;
403
398
  declare namespace FormContainer {
404
399
  var displayName: string;
405
- var whyDidYouRender: boolean;
406
400
  }
407
401
 
408
402
  type FormContextProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules> = {
@@ -453,9 +447,10 @@ type OptionalWrapperProps<TWrapper extends ComponentType<{
453
447
  * )
454
448
  * ```
455
449
  */
456
- declare const OptionalWrapper: React.FC<OptionalWrapperProps> & {
457
- whyDidYouRender: boolean;
458
- };
450
+ declare function OptionalWrapper({ condition, Wrapper, wrapperProps, children, }: OptionalWrapperProps): string | number | bigint | boolean | react_jsx_runtime.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode>>;
451
+ declare namespace OptionalWrapper {
452
+ var displayName: string;
453
+ }
459
454
 
460
455
  /**
461
456
  * Creates a splitting context with the given default value.
@@ -588,6 +583,57 @@ type StatesWithSetters<T> = T & StateSetters<T>;
588
583
  */
589
584
  declare function useSplittingState<T extends Record<string, unknown>>(initialStates: T): StatesWithSetters<T>;
590
585
 
586
+ type UseFaasStreamOptions = {
587
+ params?: Record<string, any>;
588
+ data?: string;
589
+ setData?: React.Dispatch<React.SetStateAction<string>>;
590
+ /**
591
+ * If skip is true, the request will not be sent.
592
+ *
593
+ * However, you can still use reload to send the request.
594
+ */
595
+ skip?: boolean | ((params: Record<string, any>) => boolean);
596
+ /** Send the last request after milliseconds */
597
+ debounce?: number;
598
+ baseUrl?: BaseUrl;
599
+ };
600
+ type UseFaasStreamResult = {
601
+ action: string;
602
+ params: Record<string, any>;
603
+ loading: boolean;
604
+ reloadTimes: number;
605
+ data: string;
606
+ error: any;
607
+ reload: (params?: Record<string, any>) => Promise<string>;
608
+ setData: React.Dispatch<React.SetStateAction<string>>;
609
+ setLoading: React.Dispatch<React.SetStateAction<boolean>>;
610
+ setError: React.Dispatch<React.SetStateAction<any>>;
611
+ };
612
+ /**
613
+ * Stream faas server response with React hook
614
+ *
615
+ * @param action {string} action name
616
+ * @param defaultParams {object} initial action params
617
+ * @returns {UseFaasStreamResult}
618
+ *
619
+ * @example
620
+ * ```tsx
621
+ * function Chat() {
622
+ * const [prompt, setPrompt] = useState('')
623
+ * const { data, loading, reload } = useFaasStream('chat', { prompt })
624
+ *
625
+ * return (
626
+ * <div>
627
+ * <textarea value={prompt} onChange={e => setPrompt(e.target.value)} />
628
+ * <button onClick={reload} disabled={loading}>Send</button>
629
+ * <div>{data}</div>
630
+ * </div>
631
+ * )
632
+ * }
633
+ * ```
634
+ */
635
+ declare function useFaasStream(action: string, defaultParams: Record<string, any>, options?: UseFaasStreamOptions): UseFaasStreamResult;
636
+
591
637
  /**
592
638
  * Hook to store the previous value of a state or prop.
593
639
  *
@@ -621,4 +667,4 @@ declare function usePrevious<T = any>(value: T): T | undefined;
621
667
  */
622
668
  declare function useStateRef<T = any>(initialValue?: T): [T, Dispatch<SetStateAction<T>>, RefObject<T>];
623
669
 
624
- export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, type FaasDataWrapperRef, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, type FormDefaultRulesOptions, type FormElementTypes, type FormInputElementProps, FormItem, type FormItemName, type FormItemProps, type FormLabelElementProps, type FormLang, type FormProps, type FormRule, type FormRules, type InferFormRulesOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFormContext, usePrevious, useSplittingState, useStateRef, validValues, withFaasData };
670
+ export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, type FaasDataWrapperRef, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, type FormDefaultRulesOptions, type FormElementTypes, type FormInputElementProps, FormItem, type FormItemName, type FormItemProps, type FormLabelElementProps, type FormLang, type FormProps, type FormRule, type FormRules, type InferFormRulesOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, type UseFaasStreamOptions, type UseFaasStreamResult, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFaasStream, useFormContext, usePrevious, useSplittingState, useStateRef, validValues, withFaasData };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FaasBrowserClient } from '@faasjs/browser';
2
2
  import { useState, useImperativeHandle, cloneElement, forwardRef, useEffect, useMemo, createContext, useRef, useCallback, Component, useContext } from 'react';
3
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
4
 
5
5
  // src/client.tsx
6
6
  var AsyncFunction = (async () => {
@@ -105,8 +105,7 @@ var FaasDataWrapper = fixedForwardRef(
105
105
  }
106
106
  );
107
107
  Object.assign(FaasDataWrapper, {
108
- displayName: "FaasDataWrapper",
109
- whyDidYouRender: true
108
+ displayName: "FaasDataWrapper"
110
109
  });
111
110
  function withFaasData(Component2, faasProps) {
112
111
  return (props) => /* @__PURE__ */ jsx(FaasDataWrapper, { ...faasProps, children: /* @__PURE__ */ jsx(Component2, { ...props }) });
@@ -265,10 +264,8 @@ function useConstant(fn) {
265
264
  }
266
265
  return ref.current.v;
267
266
  }
268
- useConstant.whyDidYouRender = true;
269
267
  var ErrorBoundary = class extends Component {
270
268
  static displayName = "ErrorBoundary";
271
- static whyDidYouRender = true;
272
269
  constructor(props) {
273
270
  super(props);
274
271
  this.state = {
@@ -347,7 +344,6 @@ function createSplittingContext(defaultValue) {
347
344
  return children;
348
345
  }
349
346
  Provider.displayName = "SplittingContextProvider";
350
- Provider.whyDidYouRender = true;
351
347
  function use() {
352
348
  return useConstant(() => {
353
349
  const obj = /* @__PURE__ */ Object.create(null);
@@ -364,7 +360,6 @@ function createSplittingContext(defaultValue) {
364
360
  return Object.freeze(obj);
365
361
  });
366
362
  }
367
- use.whyDidYouRender = true;
368
363
  return {
369
364
  Provider,
370
365
  use
@@ -433,20 +428,17 @@ function FormInput({
433
428
  );
434
429
  }
435
430
  FormInput.displayName = "FormInput";
436
- FormInput.whyDidYouRender = true;
437
431
  function FormItem(props) {
438
432
  const { Elements, errors } = useFormContext();
439
433
  const Label = props.label?.Label ?? Elements.Label;
440
434
  return /* @__PURE__ */ jsx(Label, { name: props.name, ...props.label, error: errors[props.name], children: /* @__PURE__ */ jsx(FormInput, { name: props.name, rules: props.rules, ...props.input }) });
441
435
  }
442
436
  FormItem.displayName = "FormItem";
443
- FormItem.whyDidYouRender = true;
444
437
  function FormBody() {
445
438
  const { items } = useFormContext();
446
439
  return items.map((item) => /* @__PURE__ */ jsx(FormItem, { ...item }, item.name));
447
440
  }
448
441
  FormBody.displayName = "FormBody";
449
- FormBody.whyDidYouRender = true;
450
442
  var FormButtonElement = forwardRef(({ children, submit, submitting, ...props }, ref) => /* @__PURE__ */ jsx(
451
443
  "button",
452
444
  {
@@ -459,10 +451,8 @@ var FormButtonElement = forwardRef(({ children, submit, submitting, ...props },
459
451
  }
460
452
  ));
461
453
  FormButtonElement.displayName = "FormButtonElement";
462
- Object.assign(FormButtonElement, { whyDidYouRender: true });
463
454
  var FormInputElement = forwardRef(({ onChange, ...props }, ref) => /* @__PURE__ */ jsx("input", { ...props, onChange: (e) => onChange(e.target.value), ref }));
464
455
  FormInputElement.displayName = "FormInputElement";
465
- Object.assign(FormInputElement, { whyDidYouRender: true });
466
456
  var FormLabelElement = ({
467
457
  name,
468
458
  title,
@@ -478,7 +468,6 @@ var FormLabelElement = ({
478
468
  ] });
479
469
  };
480
470
  FormLabelElement.displayName = "FormLabelElement";
481
- FormLabelElement.whyDidYouRender = true;
482
471
 
483
472
  // src/Form/elements/index.ts
484
473
  var FormDefaultElements = {
@@ -556,7 +545,6 @@ function FormFooter() {
556
545
  return MemoizedButton;
557
546
  }
558
547
  FormFooter.displayName = "FormFooter";
559
- FormFooter.whyDidYouRender = true;
560
548
 
561
549
  // src/Form/lang.ts
562
550
  var FormDefaultLang = {
@@ -608,12 +596,140 @@ function FormContainer({
608
596
  );
609
597
  }
610
598
  FormContainer.displayName = "FormContainer";
611
- FormContainer.whyDidYouRender = true;
612
- var OptionalWrapper = ({ condition, Wrapper, wrapperProps, children }) => {
613
- return condition ? /* @__PURE__ */ jsx(Wrapper, { ...wrapperProps, children }) : /* @__PURE__ */ jsx(Fragment, { children });
614
- };
599
+ function OptionalWrapper({
600
+ condition,
601
+ Wrapper,
602
+ wrapperProps,
603
+ children
604
+ }) {
605
+ if (condition) return /* @__PURE__ */ jsx(Wrapper, { ...wrapperProps, children });
606
+ return children;
607
+ }
615
608
  OptionalWrapper.displayName = "OptionalWrapper";
616
- OptionalWrapper.whyDidYouRender = true;
609
+ function useFaasStream(action, defaultParams, options = {}) {
610
+ const [loading, setLoading] = useState(true);
611
+ const [data, setData] = useState(options.data || "");
612
+ const [error, setError] = useState();
613
+ const [params, setParams] = useState(defaultParams);
614
+ const [reloadTimes, setReloadTimes] = useState(0);
615
+ const [fails, setFails] = useState(0);
616
+ const [skip, setSkip] = useState(
617
+ typeof options.skip === "function" ? options.skip(defaultParams) : options.skip
618
+ );
619
+ const controllerRef = useRef(null);
620
+ const pendingReloadsRef = useRef(/* @__PURE__ */ new Map());
621
+ const reloadCounterRef = useRef(0);
622
+ useEqualEffect(() => {
623
+ setSkip(
624
+ typeof options.skip === "function" ? options.skip(params) : options.skip
625
+ );
626
+ }, [typeof options.skip === "function" ? params : options.skip]);
627
+ useEqualEffect(() => {
628
+ if (!equal(defaultParams, params)) {
629
+ setParams(defaultParams);
630
+ }
631
+ }, [defaultParams]);
632
+ useEqualEffect(() => {
633
+ if (!action || skip) {
634
+ setLoading(false);
635
+ return;
636
+ }
637
+ setLoading(true);
638
+ setData("");
639
+ controllerRef.current = new AbortController();
640
+ const client = getClient(options.baseUrl);
641
+ function send() {
642
+ client.browserClient.action(action, options.params || params, {
643
+ signal: controllerRef.current.signal,
644
+ stream: true
645
+ }).then(async (response) => {
646
+ if (!response.body) {
647
+ setError(new Error("Response body is null"));
648
+ setLoading(false);
649
+ return;
650
+ }
651
+ const reader = response.body.getReader();
652
+ const decoder = new TextDecoder();
653
+ let accumulatedText = "";
654
+ try {
655
+ while (true) {
656
+ const { done, value } = await reader.read();
657
+ if (done) break;
658
+ accumulatedText += decoder.decode(value, { stream: true });
659
+ setData(accumulatedText);
660
+ }
661
+ setFails(0);
662
+ setError(null);
663
+ setLoading(false);
664
+ for (const { resolve } of pendingReloadsRef.current.values())
665
+ resolve(accumulatedText);
666
+ pendingReloadsRef.current.clear();
667
+ } catch (readError) {
668
+ reader.releaseLock();
669
+ throw readError;
670
+ }
671
+ }).catch(async (e) => {
672
+ if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0)
673
+ return;
674
+ if (!fails && typeof e?.message === "string" && e.message.indexOf("Failed to fetch") >= 0) {
675
+ console.warn(`FaasReactClient: ${e.message} retry...`);
676
+ setFails(1);
677
+ return send();
678
+ }
679
+ let error2 = e;
680
+ if (client.onError)
681
+ try {
682
+ await client.onError(action, params)(e);
683
+ } catch (newError) {
684
+ error2 = newError;
685
+ }
686
+ setError(error2);
687
+ setLoading(false);
688
+ for (const { reject } of pendingReloadsRef.current.values())
689
+ reject(error2);
690
+ pendingReloadsRef.current.clear();
691
+ return;
692
+ });
693
+ }
694
+ if (options.debounce) {
695
+ const timeout = setTimeout(send, options.debounce);
696
+ return () => {
697
+ clearTimeout(timeout);
698
+ controllerRef.current?.abort();
699
+ setLoading(false);
700
+ };
701
+ }
702
+ send();
703
+ return () => {
704
+ controllerRef.current?.abort();
705
+ setLoading(false);
706
+ };
707
+ }, [action, options.params || params, reloadTimes, skip]);
708
+ const reload = useEqualCallback(
709
+ (params2) => {
710
+ if (skip) setSkip(false);
711
+ if (params2) setParams(params2);
712
+ const reloadCounter = ++reloadCounterRef.current;
713
+ return new Promise((resolve, reject) => {
714
+ pendingReloadsRef.current.set(reloadCounter, { resolve, reject });
715
+ setReloadTimes((prev) => prev + 1);
716
+ });
717
+ },
718
+ [params, skip]
719
+ );
720
+ return {
721
+ action,
722
+ params,
723
+ loading,
724
+ data: options.data || data,
725
+ reloadTimes,
726
+ error,
727
+ reload,
728
+ setData: options.setData || setData,
729
+ setLoading,
730
+ setError
731
+ };
732
+ }
617
733
  function usePrevious(value) {
618
734
  const ref = useRef(void 0);
619
735
  useEffect(() => {
@@ -622,4 +738,4 @@ function usePrevious(value) {
622
738
  return ref.current;
623
739
  }
624
740
 
625
- export { ErrorBoundary, FaasDataWrapper, FaasReactClient, FormContainer as Form, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, FormItem, OptionalWrapper, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, useFormContext, usePrevious, useSplittingState, useStateRef, validValues, withFaasData };
741
+ export { ErrorBoundary, FaasDataWrapper, FaasReactClient, FormContainer as Form, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, FormItem, OptionalWrapper, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, useFaasStream, useFormContext, usePrevious, useSplittingState, useStateRef, validValues, withFaasData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/react",
3
- "version": "v8.0.0-beta.2",
3
+ "version": "v8.0.0-beta.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -30,10 +30,10 @@
30
30
  "dist"
31
31
  ],
32
32
  "peerDependencies": {
33
- "@faasjs/browser": ">=v8.0.0-beta.2"
33
+ "@faasjs/browser": ">=v8.0.0-beta.4"
34
34
  },
35
35
  "devDependencies": {
36
- "@faasjs/browser": ">=v8.0.0-beta.2",
36
+ "@faasjs/browser": ">=v8.0.0-beta.4",
37
37
  "@types/react": "^19.0.0",
38
38
  "react": "^19.0.0"
39
39
  },