From c3199c2423a5ea9f06d2181d2668a4f446d04ed5 Mon Sep 17 00:00:00 2001 From: Dor Shtaif Date: Thu, 19 Dec 2024 14:30:10 +0200 Subject: [PATCH] add JSDocs around the `` component --- src/Iterate/index.tsx | 145 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 5 deletions(-) diff --git a/src/Iterate/index.tsx b/src/Iterate/index.tsx index 7d66708..ea724ba 100644 --- a/src/Iterate/index.tsx +++ b/src/Iterate/index.tsx @@ -3,6 +3,105 @@ import { useAsyncIter, type IterationResult } from '../useAsyncIter/index.js'; export { Iterate, type IterateProps }; +/** + * The `` helper component is used to format and render an async iterable (or a plain non-iterable value) + * directly onto a piece of UI. + * + * Essentially wraps a single {@link useAsyncIter `useAsyncIter`} hook call into a component + * conveniently. + * + * _Illustration:_ + * + * ```tsx + * import { Iterate } from 'react-async-iterators'; + * + * function SelfUpdatingTodoList(props) { + * return ( + *
+ *

My TODOs

+ * + *
+ * Last TODO was completed at: {props.lastCompletedTodoDate} + *
+ * + *
    + * + * {({ value: todos }) => + * todos?.map(todo => + *
  • {todo.text}
  • + * ) + * } + *
    + *
+ *
+ * ); + * } + * ``` + * + * `` may be preferable over {@link useAsyncIter `useAsyncIter`} typically as the UI area it + * controls the rendering for is constrainable down to the essential, saving some React elements from + * unnecessary re-renderings while placable clearly and elegantly within some larger component's UI + * output. In regard to {@link useAsyncIter `useAsyncIter`} being a hook though, it has to + * re-render the entire component output for every new value. + * + * Given an async iterable as the `value` prop, this component will iterate it and render each new + * value that becomes available together with any possible completion or error it may run into. + * If `value` is a plain (non async iterable) value, it will simply be rendered over as-is. + * + * Whenever given `value` is changed from the previous one seen, `` will close the previous + * if it was async iterable before proceeding to iterate the new `value`. Care should be taken to + * avoid passing a constantly recreated iterable object across re-renders, e.g; by declaring it outside the component body or control __when__ it + * should be recreated with React's [`useMemo`](https://react.dev/reference/react/useMemo). + * `` will automatically close its iterated iterable as soon as it gets unmounted. + * + * @template TVal The type of values yielded by the passed iterable or otherwise type of the passed plain value itself. + * @template TInitialVal The type of the initial value, defaults to `undefined`. + * + * @param props Props for ``. See {@link IterateProps `IterateProps`}. + * + * @returns A renderable output that's re-rendered as consequent values become available and + * formatted by the function passed as `children` (or otherwise the resolved values as-are). + * + * @see {@link IterationResult} + * + * @example + * ```tsx + * // With the `initialValue` prop and showing usage of all properties of the iteration object + * // within the child render function: + * + * import { Iterate } from 'react-async-iterators'; + * + * function SelfUpdatingTodoList(props) { + * return ( + *
+ *

My TODOs

+ * + * + * {todosNext => + * todosNext.pendingFirst ? ( + *
Loading first todos...
+ * ) : ( + * <> + * {todosNext.error ? ( + *
An error was encountered: {todosNext.error.toString()}
+ * ) : ( + * todosNext.done &&
No additional updates for todos are expected
+ * )} + * + *
    + * {todosNext.map(todo => ( + *
  • {todo.text}
  • + * ))} + *
+ * + * ) + * } + *
+ *
+ * ); + * } + * ``` + */ function Iterate(props: IterateProps): ReactNode { const renderOutput = typeof props.children === 'function' @@ -12,7 +111,7 @@ function Iterate(props: IterateProps { - const propsBetterTyped = props as IteratePropsWithIterableAsChildren; + const propsBetterTyped = props as IteratePropsWithNoRenderFunction; const next = useAsyncIter(propsBetterTyped.children, propsBetterTyped.initialValue); return next.value; })(); @@ -20,18 +119,54 @@ function Iterate(props: IterateProps`} component. + * The component accepts its props in two variants: + * + * 1. Providing a render function as `children` to dynamically format each state of the iteration. + * 2. Providing an async iterable as `children` to render the values of the async iterable (or plain value) directly as are. + * + * @template TVal The type of values yielded by the passed iterable or otherwise type of the passed plain value itself. + * @template TInitialVal The type of the initial value, defaults to `undefined`. + */ type IterateProps = | IteratePropsWithRenderFunction - | IteratePropsWithIterableAsChildren; + | IteratePropsWithNoRenderFunction; type IteratePropsWithRenderFunction = { - initialValue?: TInitialVal; + /** + * The source value to iterate over, an async iterable or a plain (non async iterable) value. + */ value: TVal; + /** + * An optional initial value, defaults to `undefined`. + */ + initialValue?: TInitialVal; + /** + * A render function that is called for each iteration state and returns something to render + * out of it. + * + * @param nextIterationState - The current state of the iteration, including the yielded value, whether iteration is complete, any associated error, etc. (see {@link IterationResult `IterationResult`}) + * @returns The content to render for the current iteration state. + * + * @see {@link IterateProps `IterateProps`} + * @see {@link IterationResult `IterationResult`} + */ children: (nextIterationState: IterationResult) => ReactNode; }; -type IteratePropsWithIterableAsChildren = { - initialValue?: ReactNode; +type IteratePropsWithNoRenderFunction = { + /** + * The `value` prop source value should not be provided for this variant since it is already + * passed via `children` (see {@link IterateProps `IterateProps`}). + */ value?: undefined; + /** + * An optional initial value, defaults to `undefined`. + */ + initialValue?: ReactNode; + /** + * The source value to render from, either an async iterable to iterate over of a plain value. + */ children: ReactNode | AsyncIterable; };