UNPKG

react-async-iterators

Version:

The magic of JavaScript async iterators in React ⛓️ 🧬 🔃

156 lines 6.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IterateMulti = IterateMulti; const index_js_1 = require("../useAsyncIterMulti/index.js"); // TODO: The initial values should be able to be given in function/s form, with consideration for iterable sources that could be added in dynamically. /** * The `<IterateMulti>` component (also exported as `<ItMulti>`) is used to combine and render * any number of async iterables (or plain non-iterable values) directly onto a piece of UI. * * It's similar to `<Iterate>`, only it works with any changeable number of async iterables or plain values * instead of a single one. Essentially, can be seen as a {@link useAsyncIterMulti `useAsyncIterMulti`} * hook in a component form, conveniently. * * --- * * _Illustration:_ * * @example * ```tsx * import { useMemo } from 'react'; * import { ItMulti } from 'react-async-iterators'; * * function MyComponent() { * const numberIter = useMemo(() => createNumberIter(), []); * const arrayIter = useMemo(() => createArrayIter(), []); * return ( * <main> * <Header /> * <SideMenu /> * <ItMulti values={[numberIter, arrayIter]} initialValues={[0, []]}> * {([numState, arrState]) => ( * <> * <div> * {numState.pendingFirst * ? '⏳ Loading number...' * : `Current number: ${numState.value}`} * </div> * <div> * {arrState.pendingFirst * ? '⏳ Loading items...' * : arrState.value.map((item, i) => <div key={i}>{item}</div>)} * </div> * </> * )} * </ItMulti> * </main> * ) * } * ``` * * `<IterateMulti>` may be preferable over the {@link useAsyncIterMulti `useAsyncIterMulti`} 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 useAsyncIterMulti `useAsyncIterMulti`} being a hook it has to re-render an entire component's * output for every new value. * * Given an array of source async iterables (or plain values) as the `values` prop, this hook will start * iterating each of them concurrently, re-rendering every time any source yields a value, completes, or * errors outs. This will run the render function provided as children to `<IterateMulti>` with an array * argument that includes all latest individual states of current sources. * * `values` may also be mixed with plain (non async iterable) values, in which case they'll simply be * returned as-are, coinciding along current values of other async iterables. * This can enable components that can work seamlessly with either _"static"_ and _"changing"_ values * and props. * * Semantics are similar with `<Iterate>` - the component maintains its iteration process with each async * iterable object as long as that same object remains present in the arrays passed to the `values` prop * across subsequent updates. Changing the position of such object in the array on a consequent call will * __not__ close its current running iteration - it will only change the position its result appears at * in the array argument passed into the render function. * Care should be taken therefore to not unintentionally recreate the given iterables on every render, * by e.g; declaring an iterable outside the component body, controling __when__ it should be recreated * with React's [`useMemo`](https://react.dev/reference/react/useMemo) or preferably use the library's * {@link iterateFormatted `iterateFormatted`} util for formatting an iterable's values while preserving * its identity. * * Whenever `<IterateMulti>` is updated with a new inputs array and detects that one or more async * iterables from the previously given array are no longer present, it will close their iteration * processes. On component unmount, the hook will ensure closing all active iterated async iterables * entirely. * * --- * * @template TVals The type of the input set of async iterable or plain values as an array/tuple. * @template TInitVals The type of all initial values for each of the input values as an array/tuple, corresponding by order. * @template TDefaultInitVal The type of the default initial value (the fallback from `TValues`). `undefined` by default. * * @param props Props for `<IterateMulti>`. See {@link IterateMultiProps `IterateMultiProps`}. * * @returns A React node that re-renders itself whenever any of the source inputs change's state, and formatted by the child render function passed into `children`. * * @see {@link IterationResultSet `IterationResultSet`} * * @example * ```tsx * // Using `<ItMulti>` with a dynamically changing amount of inputs: * * import { useState } from 'react'; * import { ItMulti, type MaybeAsyncIterable } from 'react-async-iterators'; * * function DynamicInputsComponent() { * const [inputs, setInputs] = useState<MaybeAsyncIterable<string>[]>([]); * * const addAsyncIterValue = () => { * const iterableValue = (async function* () { * for (let i = 0; i < 10; i++) { * await new Promise(resolve => setTimeout(resolve, 500)); * yield `Item ${i}`; * } * })(); * setInputs(prev => [...prev, iterableValue]); * }; * * const addStaticValue = () => { * const staticValue = `Static ${inputs.length + 1}`; * setInputs(prev => [...prev, staticValue]); * }; * * return ( * <div> * <h3>Dynamic Concurrent Async Iteration</h3> * * <button onClick={addAsyncIterValue}>🔄 Add Async Iterable</button> * <button onClick={addStaticValue}>🗿 Add Static Value</button> * * <ul> * <ItMulti values={inputs} defaultInitialValue=""> * {states => * states.map((state, i) => ( * <li key={i}> * {state.done * ? state.error * ? `Error: ${state.error}` * : 'Done' * : state.pendingFirst * ? 'Pending...' * : `Value: ${state.value}`} * </li> * )) * } * </ItMulti> * </ul> * </div> * ); * } * ``` */ function IterateMulti(props) { const nexts = (0, index_js_1.useAsyncIterMulti)(props.values, { initialValues: props.initialValues, defaultInitialValue: props.defaultInitialValue, }); return props.children(nexts); } //# sourceMappingURL=index.js.map