From a45c9485d18c776e94ab04f46f72554fc5150780 Mon Sep 17 00:00:00 2001
From: Dor Shtaif
Date: Tue, 28 Jan 2025 12:35:45 +0200
Subject: [PATCH 01/11] various `README.md` edits
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 56b1452..700190e 100644
--- a/README.md
+++ b/README.md
@@ -654,10 +654,10 @@ Essentially, can be seen as a [`useAsyncIter`](#useasynciter) hook in a componen
The source value to iterate over - an async iterable or a plain (non async iterable) value. The input value may be changed any time, starting a new iteration in the background, per [Iteration lifecycle](#iteration-lifecycle). If using the "simplified" form, `value` is ignored and the source value should be provided as `children` instead.
- `initialValue`:
- An optional starting value, defaults to `undefined`. Will be the value inserted into the child render function when [``](#it) first renders during mount and while it's pending first yield. You can pass an actual value, or a function that returns a value (which [``](#it) will call once during mounting).
+ An _optional_ starting value, defaults to `undefined`. Will be the value inserted into the child render function when [``](#it) first renders during mount and while it's pending first yield. You can pass an actual value, or a function that returns a value (which [``](#it) will call once during mounting).
- `children`:
- A render function that is called for each step of the iteration, returning something to render out of it, with the current state object as the argument (see [Iteration state properties breakdown](#iteration-state-properties-breakdown)). If using the "simplified" form, the source value is directly passed as children and yielded values are rendered just as-are without any formatting on top of.
+ A render function that is called for each step of the iteration, returning something to render out of it, with the current state object as the argument (see [Iteration state properties breakdown](#iteration-state-properties-breakdown)). If using the "simplified" form instead - the source value should be directly passed as children and yielded values are rendered just as-are without any formatting on top.
### Notes
@@ -896,7 +896,7 @@ next.error;
The source value to iterate over - an async iterable or a plain (non async iterable) value. The input value may be changed any time, starting a new iteration in the background, per [Iteration lifecycle](#iteration-lifecycle).
- `initialValue`:
- An optional starting value for the hook to return prior to the ___first yield___ of the ___first given___ async iterable, defaults to `undefined`. You can pass an actual value, or a function that returns a value (which the hook will call once during mounting).
+ An _optional_ starting value for the hook to return prior to the ___first yield___ of the ___first given___ async iterable, defaults to `undefined`. You can pass an actual value, or a function that returns a value (which the hook will call once during mounting).
### Returns
@@ -1151,7 +1151,7 @@ function handleValueSubmit() {
### Parameters
- `initialValue`:
- Any optional starting value for the state iterable's `.value.current` property, defaults to `undefined`. You can pass an actual value, or a function that returns a value (which the hook will call once during mounting).
+ Any _optional_ starting value for the state iterable's `.value.current` property, defaults to `undefined`. You can pass an actual value, or a function that returns a value (which the hook will call once during mounting).
### Returns
From 9ab88654cd7817414586186aff68edb1de965063 Mon Sep 17 00:00:00 2001
From: Dor Shtaif
Date: Tue, 28 Jan 2025 12:51:54 +0200
Subject: [PATCH 02/11] more readme edits
---
README.md | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 700190e..7df712c 100644
--- a/README.md
+++ b/README.md
@@ -488,8 +488,11 @@ function MyComponent() {
}
```
->
ℹ️ Each call to [`iterateFormatted`](#iterateformatted) returns a _formatted_ versions of `currenciesIter` with some transparent metadata, which the library's consumers (like [``](#it)) use to associate every transformed iterable with its original source iterable, and this way existing iteration states can be persisted properly. It's therefore safe to recreate and pass on formatted iterables from repeated calls to [`iterateFormatted`](#iterateformatted) across re-renders (as long the same source is used with it consistently).
-So unless you require some more elaborate transformation than simply formatting values - it might be more ergonomic to use [`iterateFormatted`](#iterateformatted) vs manual compositions within [`React.useMemo`](https://react.dev/reference/react/useMemo), especially if dealing with multiple iterables to transform.
+
+
+>
ℹ️ As seen above, unless you require some more elaborate transformation than simply formatting values - it might be more ergonomic to use [`iterateFormatted`](#iterateformatted) vs manual compositions within [`React.useMemo`](https://react.dev/reference/react/useMemo)s - especially if you're having to transform multiple iterables.
+
+Every call to [`iterateFormatted`](#iterateformatted) returns a _formatted_ versions of `currenciesIter` with some transparent metadata, which the library's consumers (like [``](#it)) use to associate every transformed iterable with its original source iterable, and this way existing iteration states can be persisted properly. It's therefore safe to recreate and pass on formatted iterables from repeated calls to [`iterateFormatted`](#iterateformatted) across re-renders (as long the same source is used with it consistently).
From 2def7f98d8a73962efd3fc7e1656053d84b9add4 Mon Sep 17 00:00:00 2001
From: Dor Shtaif
Date: Fri, 31 Jan 2025 19:21:31 +0200
Subject: [PATCH 03/11] edit `README.md`
---
README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 7df712c..769173d 100644
--- a/README.md
+++ b/README.md
@@ -500,7 +500,7 @@ Every call to [`iterateFormatted`](#iterateformatted) returns a _formatted_ vers
-As illustrated throughout this library and docs - when dealing with data in your app that's presented as an async iterable, an interesting pattern emerges; instead of a transition in app state traditionally sending down a cascading re-render through the entire tree of components underneath it to propagate the new state - your __async iterable__ objects can be distributed __once__ when the whole tree is first mounted, and when new data is then communicated through them it directly gets right to the edges of the UI tree that are concerned with it, re-rendering them exclusively and thus skipping all intermediaries.
+As illustrated throughout this library and docs - when dealing with data in your app that's presented as an async iterable, an interesting pattern emerges; instead of a transition in app state traditionally sending down a cascading re-render through the entire tree of components underneath it to propagate the new state - your __async iterable__ objects can be distributed __once__ when the whole tree is first mounted, and new data is then communicated through them so it directly gets to the components that are concerned with displaying it, at the edges of the UI tree, re-rendering them exclusively and thus skipping all intermediaries.
The packaged [`useAsyncIterState`](#useasynciterstate) hook can lend this paradigm to your __component state__. It's like a [`React.useState`](https://react.dev/reference/react/useState) version that returns you _an async iterable of the state value instead of the state value_, paired with a setter function that causes the stateful iterable to yield the next states.
@@ -554,6 +554,10 @@ function MyForm() {
}
```
+_Play with [`useAsyncIterState`](#useasynciterstate) an interactive example:_
+
+[](https://stackblitz.com/edit/react-async-iterators-example-4?file=src%2FApp.tsx)
+
From 6e5ce354a84e2b0ef603c4f7bc6245a6841fb505 Mon Sep 17 00:00:00 2001
From: Dor Shtaif
Date: Tue, 4 Feb 2025 12:58:51 +0200
Subject: [PATCH 04/11] fix StackBlitz link adjacent text
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 769173d..3ffebab 100644
--- a/README.md
+++ b/README.md
@@ -554,7 +554,7 @@ function MyForm() {
}
```
-_Play with [`useAsyncIterState`](#useasynciterstate) an interactive example:_
+_Play with [`useAsyncIterState`](#useasynciterstate) inside a StackBlitz playground:_
[](https://stackblitz.com/edit/react-async-iterators-example-4?file=src%2FApp.tsx)
From 304407a9d61bdc8b63294ceadc19f81d4684f943 Mon Sep 17 00:00:00 2001
From: Dor Shtaif
Date: Mon, 17 Feb 2025 17:58:14 +0200
Subject: [PATCH 05/11] various `README.md` edits
---
README.md | 112 +++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 86 insertions(+), 26 deletions(-)
diff --git a/README.md b/README.md
index abc3ea2..5357e7a 100644
--- a/README.md
+++ b/README.md
@@ -2,31 +2,20 @@
> Hooks, components and utilities for working with JavaScript [async iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator) values in React.js.
-
-
- [](https://www.npmjs.com/package/react-async-iterators)
+ [](https://www.npmjs.com/package/react-async-iterators)
[](https://github.com/shtaif/react-async-iterators/actions/workflows/ci-run-tests.yaml)
[](https://github.com/shtaif/react-async-iterators/actions/workflows/ci-ts-build-check.yaml)
[](https://semver.org)
- [](https://github.com/shtaif/react-async-iterators/blob/master/LICENSE.txt)
+ [](https://github.com/shtaif/react-async-iterators/blob/master/LICENSE.txt)
-Async iterables/iterators are a native language construct in JS that can be viewed as a counterpart to [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), in the sense that while a promise asynchronously resolves one value - an async iterable is a stream that asynchronously yields any number of values.
-
-Somewhat obvious to say, the React ecosystem features many methods and tools that have to do with integrating promise-based data into your React components; from higher level SDK libraries, state managers - to generic async utilities, which make the different promise states available to the rendering. And just like that - `react-async-iterators` packs hooks, components and utilities written in TypeScript with the aim to make async iterables into __first-class citizens to React__ as they become gradually more prevalent across JavaScript platforms.
+A React.js library that makes it __easy and satisfying__ to integrate and render JS async iterators across and throughout your app's components. Expanding from that, it allows you to describe and propagate various aspects and states of your app in actual async iterator form, letting it tap into the full benefits and flexibility in this JS construct.
-## What can `react-async-iterators` be used for?
-
-- easily consuming async iterables obtained from any library, web API or composed manually - in a React-friendly declarative fashion.
-
-- unlocking new ways of expressing data flow in or between components efficiently, constricting redundant re-rendering.
-
-
-
### Illustration:
@@ -69,8 +58,27 @@ const randoms = {
// etc.
```
-
-
+
+
+```
+-->
@@ -103,7 +112,7 @@ function LiveUserProfile(props: { userId: string }) {
✔️ Fully written in TypeScript with comprehensive inferring typings
✔️ Fully tree-shakeable exports
-✔️ Light weight, zero run-time dependencies
+✔️ Light weight, ZERO run-time dependencies
✔️ ESM build
✔️ [Semver](https://semver.org) compliant
@@ -111,15 +120,19 @@ function LiveUserProfile(props: { userId: string }) {
# Table of Contents
+- [Introduction](#introduction)
+ - [Who is react-async-iterators for?](#who-is-react-async-iterators-for)
+ - [When should you use react-async-iterators?](#when-should-you-use-react-async-iterators)
+ - [What can react-async-iterators do?](#what-can-react-async-iterators-do)
- [Installation](#installation)
-- [Walkthrough](#walkthrough)
+- [Overview](#overview)
- [Consuming async iterables](#consuming-async-iterables)
- [Plain values](#plain-values)
- [Iteration lifecycle](#iteration-lifecycle)
- [Lifecycle phases](#lifecycle-phases)
- [Async iterables with current values](#async-iterables-with-current-values)
- [Formatting values](#formatting-values)
- - [Component state as an async iterable](#component-state-as-an-async-iterable)
+ - [State as an async iterable](#state-as-an-async-iterable)
- [API](#api)
- [Iteration state properties breakdown](#iteration-state-properties-breakdown)
- [Components](#components)
@@ -136,6 +149,49 @@ function LiveUserProfile(props: { userId: string }) {
+# Introduction
+
+Async iterables and iterators are a native JavaScript construct that can be seen as a counterpart to [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)s in a way that is captured by the following:
+
+> _A promise resolves __a single__ value asynchronously, whereas an async iterable is a stream yielding __any number__ of values asynchronously._
+
+Slightly obvious to say, the React ecosystem is featuring many methods and tools that have to do with integration of promise-based data into your React components; from higher level SDK libraries, state managers - to generic async utilities, which make the different promise states accessible during render. And to the same extent, `react-async-iterators` packs hooks, components and utilities, written in 100% TypeScript, with the aim to make async iterables into __first-class citizens to React__ as they become gradually more prevalent across the JavaScript ecosystem.
+
+
+
+
+
+## Who is `react-async-iterators` for?
+
+`react-async-iterators` is designed for React developers aiming to seamlessly integrate asynchronous data streams into their apps, as well as enhance and optimize how they build their interactive data-driven apps in general. This library offers a declarative approach to manage real-time updates, push or pull based data sources, or any asynchronous series of values easily and effectively within React components.
+
+
+
+## When should you use `react-async-iterators`?
+
+- When integrating any async iterable obtained from a library, a web API, or composed manually.
+
+- When apps involve any _asynchronously-generated series_ of data, such as data updated via recurring timers, WebSocket messages, GraphQL subscriptions, Geolocation watching and more...
+
+- When rendering a complex form or dynamic widget with large nested component tree for which UI updates might impact UI performance.
+
+
+
+## What can `react-async-iterators` do?
+
+- Easily render any async iterable in a declarative, React-friendly style.
+
+
+- Convert any series of data into an async iterable, enabling it the full functionality of this library.
+
+- Unlock new patterns for expressing data flow within and between components which greatly minimize redundant re-renders by embracing async iterables __as data__.
+
+- Compose and refine your data in the form of async iterables, enabling specialized behaviors and optimizations in propagating your data which are otherwise very hard to achieve in a typical React environment.
+
+- Build better apps and components by relying on async iterables' consistent semantics for completion and error, composability and resource encapsulation. Handle any async sequence of values perceivable via a single generic interface; ___the native one___, instead of grasping various methods and opinionated APIs coupled to every type of operation.
+
+
+
# Installation
@@ -159,7 +215,7 @@ import { It, type IterationResult } from 'react-async-iterators';
-# Walkthrough
+# Overview
@@ -399,7 +455,7 @@ When any consumer hook/component from the library detects the presence of a curr
This rule bridges the gap between async iterables which always yield asynchronously (as their yields are wrapped in promises) and React's component model in which render outputs are strictly synchronous. Due to this discrepency, for example, if the first value for an async iterable is known in advance and yielded as soon as possible - React could only grab the yielded value from it via a subsequent (immediate) run/render of the consumer hook/component (since the promise can resolve only _after_ such initial sync run/render). This issue is therefore solved by async iterables that expose a current value.
-For example, the stateful iterable created from the [`useAsyncIterState`](#useasynciterstate) hook (_see [Component state as an async iterable](#component-state-as-an-async-iterable)_) applies this convention from its design, acting like a "topic" with an always-available current value that's able to signal out future changes, skipping pending phases, so there's no need to set initial starting states.
+For example, the stateful iterable created from the [`useAsyncIterState`](#useasynciterstate) hook (_see [State as an async iterable](#state-as-an-async-iterable)_) applies this convention from its design, acting like a "topic" with an always-available current value that's able to signal out future changes, skipping pending phases, so there's no need to set initial starting states.
@@ -457,7 +513,7 @@ function MyComponent() {
}
```
-Alternatively, such transformation can be also achieved (_entirely legitimately_) with help from [`React.useMemo`](https://react.dev/reference/react/useMemo) and some generic mapping operator like [`iter-tools`](https://github.com/iter-tools/iter-tools)'s `asyncMap`, among the multitude of available operators from such libraries:
+Alternatively, such transformation can be also achieved (_entirely legitimately_) with help from [`React.useMemo`](https://react.dev/reference/react/useMemo) and some generic mapping operator like [`iter-tools`](https://github.com/iter-tools/iter-tools)'s `asyncMap`, among the breadth of operators available from such libraries:
```tsx
import { useMemo } from 'react';
@@ -492,7 +548,7 @@ Every call to [`iterateFormatted`](#iterateformatted) returns a _formatted_ vers
-## Component state as an async iterable
+## State as an async iterable
@@ -523,7 +579,7 @@ function MyCounter() {
}
```
-The stateful iterable let's you directly access the current state any time via its `.value.current` property (see [Async iterables with current values](#async-iterables-with-current-values)) so you may read it when you need to get only the current state alone, for example - as part of a certain side effect logic;
+The stateful iterable let's you access the current state any time via its `.value.current` property (see [Async iterables with current values](#async-iterables-with-current-values)) so you may read it when you need to get only the current state alone, for example - as part of some side effect logic;
```tsx
// Using the state iterable's `.value.current` property to read the immediate current state:
@@ -550,6 +606,10 @@ function MyForm() {
}
```
+
+
+>
ℹ️ Remember that merely accessing `.value.current` will only get the current state at that point in time and is not meant for picking up future updates in it. To be in sync with current and future states, simply render the state iterable through a [``](#it).
+
_Play with [`useAsyncIterState`](#useasynciterstate) inside a StackBlitz playground:_
[](https://stackblitz.com/edit/react-async-iterators-example-4?file=src%2FApp.tsx)
From 5286535e8bbb9d71d9f6254d5e3390ce9237da8f Mon Sep 17 00:00:00 2001
From: Dor Shtaif
Date: Wed, 19 Feb 2025 19:13:16 +0200
Subject: [PATCH 06/11] `README.md` edits
---
README.md | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
index 5357e7a..96b2e9f 100644
--- a/README.md
+++ b/README.md
@@ -13,11 +13,15 @@
-A React.js library that makes it __easy and satisfying__ to integrate and render JS async iterators across and throughout your app's components. Expanding from that, it allows you to describe and propagate various aspects and states of your app in actual async iterator form, letting it tap into the full benefits and flexibility in this JS construct.
+A React.js library that makes it __easy and satisfying__ to integrate and render JS async iterators across and throughout your app's components. Expanding from that, it enables you to describe and propagate states and various aspects of your app in actual async iterator form, tapping into the full benefits and flexibility of this JS construct.
+The goal behind this library is to promote a mental model where fundamentally every piece of data in a JavaScript program can be expressed in either a plain and static form, or in a ___dynamic, self-evolving form - an async iterable___. By simply wrapping a value in an async iterator or iterable, it becomes a self-updating entity while remaining first-class data. From this, it follows naturally that every interface should and could accommodate either kind of inputs, seamlessly adapting to changes over time as intuitively expected.
+To facilitate this, `react-async-iterators` offers a set of tools specifically tailored for the frontend and React which and embraces composability with the upcoming standardization of [Async Iterator Helpers proposal](https://github.com/tc39/proposal-async-iterator-helpers) as well as projects such as [iter-tools](https://github.com/iter-tools/iter-tools), [IxJS](https://github.com/ReactiveX/IxJS) and more.
-### Illustration:
+
+
+### Illustration
[](https://stackblitz.com/edit/react-async-iterators-example-3?file=src%2FApp.tsx)
@@ -27,7 +31,7 @@ import { It } from 'react-async-iterators';
const randoms = {
async *[Symbol.asyncIterator]() {
while (true) {
- await new Promise((r) => setTimeout(r, 500));
+ await new Promise(r => setTimeout(r, 500));
const x = Math.random();
yield Math.round(x * 10);
}
@@ -58,11 +62,15 @@ const randoms = {
// etc.
```
-Below is another interactive demo showing how to consume the `EventSource` web API (A.K.A [SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)) converted into async iterable using the [`iterified`](https://github.com/shtaif/iterified) package:
-[](https://stackblitz.com/edit/react-async-iterators-example-5?file=src%2FApp.tsx)
+### More examples
+
+ - Interactive demo showing how to consume the `EventSource` web API ([Server-Sent-Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)) converted into async iterable using the [`iterified`](https://github.com/shtaif/iterified) package:
+
[](https://stackblitz.com/edit/react-async-iterators-example-5?file=src%2FApp.tsx)
+ - Interactive demo showing how to consume a [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) converted into async iterable using the [`iterified`](https://github.com/shtaif/iterified) package:
+
[](https://stackblitz.com/edit/react-async-iterators-example-6?file=src%2FApp.tsx)