Skip to content

Add JSDoc blocks for symbols related to the useAsyncIter hook #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 134 additions & 3 deletions src/useAsyncIter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,86 @@ export { useAsyncIter, type IterationResult };

// TODO: The initial value can be given as a function, which the internal `useState` would invoke as it's defined to do. So the typings should take into account it possibly being a function and if that's the case then to extract its return type instead of using the function type itself

/**
* `useAsyncIter` hooks up a single async iterable value into your component and its lifecycle.
*
* Given an async iterable `input`, this hook will iterate it and rerender the host component upon
* each new value that becomes available as well as any possible completion or error it may run into.
* If `input` is a plain (non async iterable) value, it will simply be used to render once and
* immediately.
*
* The hook inits and maintains its current iteration process across renderings as long as its
* `input` is passed the same object reference each time (similar to the behavior of a
* `useEffect(() => {...}, [input])`), therefore care should be taken to avoid constantly recreating
* the iterable every render, e.g; declaring it outside the component body or control __when__ it
* should be recreated with React's [`useMemo`](https://react.dev/reference/react/useMemo).
* Whenever `useAsyncIter` detects a different `input` value, it automatically closes a previous
* `input` async iterable before proceeding to iterate any new `input` async iterable. The hook will
* also ensure closing a currently iterated `input` on component unmount.
*
* The object returned from `useAsyncIter` holds all the state from the most recent iteration
* of `input` (most recent value, whether is completed or still running, etc. - see
* {@link IterationResult}).
* In case `input` is given a plain value, it will be delivered as-is within the returned
* result object's `value` property.
*
* @param input Any async iterable or plain value
* @param initialValue Any initial value for the hook to return prior to resolving the ___first
* emission___ of the ___first given___ async iterable, defaults to `undefined`.
*
* @returns An object with properties reflecting the current state of the iterated async iterable
* or plain value provided via `input` (see {@link IterationResult})
*
* @see {@link IterationResult}
*
* @example
* ```tsx
* // In its simplest:
*
* import { useAsyncIter } from 'react-async-iterators';
*
* function SelfUpdatingTodoList(props) {
* const { value: todos } = useAsyncIter(props.todosAsyncIter);
* return (
* <ul>
* {todos?.map(todo => (
* <li key={todo.id}>{todo.text}</li>
* ))}
* </ul>
* );
* }
* ```
*
* @example
* ```tsx
* // With an `initialValue` and showing usage of all properties of the returned iteration object:
*
* import { useAsyncIter } from 'react-async-iterators';
*
* function SelfUpdatingTodoList(props) {
* const todosNext = useAsyncIter(props.todosAsyncIter, []);
* return (
* <>
* {todosNext.error ? (
* <div>An error was encountered: {todosNext.error.toString()}</div>
* ) : todosNext.done && (
* <div>No additional updates for todos are expected</div>
* )}
*
* {todosNext.pendingFirst ? (
* <div>Loading first todos...</div>
* ) : (
* <ul>
* {todosNext.map(todo => (
* <li key={todo.id}>{todo.text}</li>
* ))}
* </ul>
* )}
* </>
* );
* }
* ```
*/
const useAsyncIter: {
<TValue>(
input: AsyncIterable<TValue>,
Expand Down Expand Up @@ -104,9 +184,60 @@ const useAsyncIter: {
}
};

type IterationResult<TValue, TInitValue = undefined> = {
/** The most recent value received */
value: ExtractAsyncIterValue<TValue> | TInitValue;
/**
* The `iterationResult` object holds all the state from the most recent iteration of a currently
* hooked async iterable object (or plain value).
*
* Returned from the {@link useAsyncIter} hook and also injected into `<Iterate>` component's render function.
*
* @see {@link useAsyncIter}
* @see {@link Iterate}
*/
type IterationResult<TVal, TInitVal = undefined> = {
/**
* The most recent value received from iterating an async iterable, starting as {@link TInitVal}.
* If iterating a plain value, it will simply be it.
*
* Starting to iterate a new async iterable at any future point on itself doesn't reset this;
* only some newly resolved next value will.
* */
value: ExtractAsyncIterValue<TVal> | TInitVal;

/**
* Indicates whether the iterated async iterable is still pending on its own first
* value to be resolved.
* Will appear `false` for any iterations thereafter and reset back every time the iteratee
* is replaced with a new one.
*
* Can be used in certain cases for displaying _"loading" states_ metaphorically similar to
* a how a pending state of a promise is thought of.
*
* Is always `false` for any plain value given instead of an async iterable.
*/
pendingFirst: boolean;

/**
* Indicates whether the iterated async iterable is done; meaning had either completed (by
* resolving a `{ done: true }` object
* [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#done))
* or threw an error (in which case the escorting `error` property will be set to it).
*
* When `true`, the adjacent `value` property will __still be set__ to the last value seen
* until the moment of completing/erroring.
*
* Is always `false` for any plain value given instead of an async iterable.
*/
done: boolean;

/**
* Indicates whether the iterated async iterable threw an error, capturing a reference to it.
*
* If `error` is non-empty, the escorting `done` property will always be `true` since the
* iteration process is considered over.
*
* Is always `undefined` for any plain value given instead of an async iterable.
*/
error: unknown;
} & (
| {
pendingFirst: true;
Expand Down
Loading