react-async-iterators
Version:
The magic of JavaScript async iterators in React ⛓️ 🧬 🔃
117 lines • 4.39 kB
JavaScript
import { useAsyncIter } from '../useAsyncIter/index.js';
export { Iterate };
/**
* The `<Iterate>` component (also exported as `<It>`) is used to format and render an async
* iterable (or a plain non-iterable value) directly onto a piece of UI.
*
* Essentially, can be seen as a {@link useAsyncIter `useAsyncIter`} hook in a component form,
* conveniently.
*
* _Illustration:_
*
* ```tsx
* import { It } from 'react-async-iterators';
*
* function SelfUpdatingTodoList(props) {
* return (
* <div>
* <h2>My TODOs</h2>
*
* <div>
* Last TODO was completed at: <It>{props.lastCompletedTodoDate}</It>
* </div>
*
* <ul>
* <It value={props.todosAsyncIter}>
* {({ value: todos }) =>
* todos?.map(todo =>
* <li key={todo.id}>{todo.text}</li>
* )
* }
* </It>
* </ul>
* </div>
* );
* }
* ```
*
* `<Iterate>` may be preferable over the {@link useAsyncIter `useAsyncIter`} counterpart typically as
* the UI area it re-renders within a component tree can be expressly confined to the necessary
* minimum, saving any other unrelated elements from re-evaluation - while on the other hand
* {@link useAsyncIter `useAsyncIter`} being a hook it has to re-render an entire
* component's output for every new value.
*
* Given an async iterable as the `value` prop, this component will iterate it and render every 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 as-is.
*
* Whenever given `value` is changed from the previous one seen, `<Iterate>` 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 by controlling __when__ it should be recreated with React's
* [`useMemo`](https://react.dev/reference/react/useMemo). `<Iterate>` 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 `<Iterate>`. See {@link IterateProps `IterateProps`}.
*
* @returns A React node that re-renders itself with each yielded value, completion or error, and formatted by the child render function passed into `children` (or just the yielded value as-is in the absent of it).
*
* @see {@link IterationResult `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 (
* <div>
* <h2>My TODOs</h2>
*
* <Iterate initialValue={[]} value={props.todosAsyncIter}>
* {todosNext =>
* todosNext.pendingFirst ? (
* <div>Loading first todos...</div>
* ) : (
* <>
* {todosNext.error ? (
* <div>An error was encountered: {todosNext.error.toString()}</div>
* ) : (
* todosNext.done && <div>No additional updates for todos are expected</div>
* )}
*
* <ul>
* {todosNext.map(todo => (
* <li key={todo.id}>{todo.text}</li>
* ))}
* </ul>
* </>
* )
* }
* </Iterate>
* </div>
* );
* }
* ```
*/
function Iterate(props) {
const renderOutput = typeof props.children === 'function'
? (() => {
const propsBetterTyped = props;
const next = useAsyncIter(propsBetterTyped.value, propsBetterTyped.initialValue);
return propsBetterTyped.children(next);
})()
: (() => {
const propsBetterTyped = props;
const next = useAsyncIter(propsBetterTyped.children, propsBetterTyped.initialValue);
return next.value;
})();
return renderOutput;
}
//# sourceMappingURL=index.js.map