Skip to content

Commit 63b3f7d

Browse files
committed
Merge branch 'master' of https://github.com/shtaif/react-async-iterators into async-iter-memo
2 parents 3fe9e8e + f19eabd commit 63b3f7d

File tree

21 files changed

+1571
-98
lines changed

21 files changed

+1571
-98
lines changed

.github/actions/ci-common-setup-locally-packaged/action.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: Reusable common setup with locally packaged library code for projec
44

55
inputs:
66
node-version:
7-
description: Specific Node.js version to override the common one that's gonna be selected by default
7+
description: Optional specific Node.js version to install (default is v22.5.0)
88
required: false
99
default: v22.5.0
1010

@@ -36,7 +36,7 @@ runs:
3636
PACKAGE_FILENAME=$(ls ${{ steps.package-json-info.outputs.package_name }}-${{ steps.package-json-info.outputs.package_version }}.tgz)
3737
pnpm i packaged-react-async-iterators@file:./$PACKAGE_FILENAME
3838
39-
- name: Type-check tests code
39+
- name: Redirect code entrypoint from the source code to packaged code
4040
shell: bash
4141
run: |
4242
[[ -e ./src ]] && mv ./src ./src-ignored-for-packaged-testing

.github/actions/ci-common-setup/action.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ description: Reusable common setup for project's CI jobs
44

55
inputs:
66
node-version:
7-
description: Specific Node.js version to override the common one that's gonna be selected by default
7+
description: Optional specific Node.js version to install (default is v22.5.0)
88
required: false
99
default: v22.5.0
1010

1111
runs:
1212
using: composite
1313
steps:
14-
- uses: actions/setup-node@v3
14+
- uses: actions/setup-node@v4
1515
with:
1616
node-version: ${{ inputs.node-version }}
1717

1818
- name: Get Node.js version
1919
id: node_version
2020
shell: bash
21-
run: echo "version=$(node -v)" >>$GITHUB_OUTPUT
21+
run: echo "version=$(node -v)" >> $GITHUB_OUTPUT
2222

2323
- name: Get package manager type and version from `package.json`
2424
id: pkg_manager_value
@@ -33,7 +33,7 @@ runs:
3333

3434
- name: Restore possibly cached dependencies
3535
id: cache-node-modules
36-
uses: actions/cache@v3
36+
uses: actions/cache@v4
3737
with:
3838
path: ./node_modules
3939
key: node-modules-${{ runner.os }}-${{ steps.node_version.outputs.version }}-${{ hashFiles('./pnpm-lock.yaml') }}

.github/workflows/ci-ts-build-check.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: CI - ts build check
1+
name: CI - TypeScript build check
22

33
on:
44
pull_request:

.releaserc.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ plugins:
2020
- { type: perf, section: Performance, hidden: false }
2121
- { type: revert, section: Reverts, hidden: false }
2222
- { type: refactor, section: Refactor, hidden: true }
23+
- { type: chore, section: Chores, hidden: true }
2324
- { type: test, section: Tests, hidden: true }
2425
- { type: ci, section: Continuous Integration, hidden: true }
2526

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## [0.10.2](https://github.com/shtaif/react-async-iterators/compare/v0.10.1...v0.10.2) (2025-02-19)
2+
3+
4+
### Documentation
5+
6+
* **readme:** various edits ([#90](https://github.com/shtaif/react-async-iterators/issues/90)) ([27a8d41](https://github.com/shtaif/react-async-iterators/commit/27a8d41f3cb3f7591c9b0566d63a9f0aa3a388ee))
7+
* **readme:** various edits for `README.md` ([#91](https://github.com/shtaif/react-async-iterators/issues/91)) ([0f91ca4](https://github.com/shtaif/react-async-iterators/commit/0f91ca4fc9e929cafbfb70b0f4b01e5b455a77b5))
8+
19
## [0.10.1](https://github.com/shtaif/react-async-iterators/compare/v0.10.0...v0.10.1) (2025-02-17)
210

311

README.md

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@
1313

1414
</p>
1515

16-
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.
16+
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.
1717

18+
To facilitate this, `react-async-iterators` offers a set of tools specifically tailored for the frontend and React while embracing composability with the upcoming standardization of [Async Iterator Helpers proposal](https://github.com/tc39/proposal-async-iterator-helpers) as well as utility libraries such as [iter-tools](https://github.com/iter-tools/iter-tools), [IxJS](https://github.com/ReactiveX/IxJS) and more. You may use this library as a one-off in your code; e.g got an async iterable from a third-party SDK and just need to consume it. You may also employ it throughout your entire app. That's up to you. The library just aims to be _"everything async iterators and React"_ and is fully tree-shakable.
1819

20+
The goal behind this library is to promote a mental model where every piece of data in a JavaScript program can be expressed either in a plain and static form, or in a ___dynamic, self-evolving___ form - an async iterable. That 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 interfaces should and could intuitively accommodate either kind as received input, and seamlessly adapt to any changes over time just as you'd expect.
1921

20-
### Illustration:
22+
The library will continue to expand with new tools over time.
23+
24+
25+
26+
### Illustration
2127

2228
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/edit/react-async-iterators-example-3?file=src%2FApp.tsx)
2329

@@ -27,7 +33,7 @@ import { It } from 'react-async-iterators';
2733
const randoms = {
2834
async *[Symbol.asyncIterator]() {
2935
while (true) {
30-
await new Promise((r) => setTimeout(r, 500));
36+
await new Promise(r => setTimeout(r, 500));
3137
const x = Math.random();
3238
yield Math.round(x * 10);
3339
}
@@ -58,12 +64,24 @@ const randoms = {
5864
// etc.
5965
```
6066

61-
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:
6267

63-
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/edit/react-async-iterators-example-5?file=src%2FApp.tsx)
6468

69+
### More examples
70+
71+
- 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:
72+
<br/><br/>[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/edit/react-async-iterators-example-5?file=src%2FApp.tsx)
73+
74+
- 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:
75+
<br/><br/>[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/edit/react-async-iterators-example-6?file=src%2FApp.tsx)
6576

6677

78+
79+
## 🔥 🔥 🔥
80+
81+
If you find this package helpful, please consider giving it a star! ⭐️<br/>
82+
Something isn't right? don't hesitate to open an issue! 👍<br/>
83+
Thanks a lot for viewing this project! 🙏🏻
84+
6785
<!--
6886
```tsx
6987
const gqlWsClient = createGqlWsClient({ url: 'wss://my-api-backend/graphql' });
@@ -141,6 +159,7 @@ function LiveUserProfile(props: { userId: string }) {
141159
- [Hooks](#hooks)
142160
- [`useAsyncIter`](#useasynciter)
143161
- [`useAsyncIterMulti`](#useasyncitermulti)
162+
- [`useAsyncIterEffect`](#useasyncitereffect)
144163
- [`useAsyncIterState`](#useasynciterstate)
145164
- [`useSharedAsyncIter`](#usesharedasynciter)
146165
- [Utils](#utils)
@@ -173,7 +192,7 @@ Slightly obvious to say, the React ecosystem is featuring many methods and tools
173192

174193
- When apps involve any _asynchronously-generated series_ of data, such as data updated via recurring timers, WebSocket messages, GraphQL subscriptions, Geolocation watching and more...
175194

176-
- When rendering a complex form or dynamic widget with large nested component tree for which UI updates might impact UI performance.
195+
- When rendering a complex form or dynamic widget with large nested component tree for which updates might impact UI performance.
177196

178197

179198

@@ -223,7 +242,7 @@ import { It, type IterationResult } from 'react-async-iterators';
223242

224243
Async iterables can be hooked into your components and consumed using [`<It>`](#it) and [`<ItMulti>`](#itmulti), or their hook counterparts [`useAsyncIter`](#useasynciter) and [`useAsyncIterMulti`](#useasyncitermulti) respectively.
225244

226-
The iteration values and states are expressed via a consistent structure (see exaustive list in [this breakdown](#iteration-state-object-detailed-breakdown)).<br/>
245+
The iteration values and states are expressed via a consistent structure (see exaustive list in [this breakdown](#iteration-state-properties-breakdown)).<br/>
227246
They may be accessed as follows:
228247

229248
```tsx
@@ -1192,6 +1211,76 @@ const [nextNum, nextStr, nextArr] = useAsyncIterMulti([numberIter, stringIter, a
11921211

11931212

11941213

1214+
### `useAsyncIterEffect`
1215+
1216+
Given some async iterables, a side-effect function and a computed list of dependencies - runs the provided side-effect whenever any of the provided dependencies change from the previously seen ones, letting you derive them from the values yielded by the async iterables.
1217+
1218+
This hook is like an _async-iterable-aware_ version for [`React.useEffect`](https://react.dev/reference/react/useEffect), allowing dependencies to be also computed from values yielded by the given async iterables each time, and letting the effect fire directly in reaction to particular async iterable yields rather than only just component scope values being changed across re-renders (as does the classic [`React.useEffect`](https://react.dev/reference/react/useEffect)).
1219+
1220+
```tsx
1221+
useAsyncIterEffect(
1222+
[fooIter, barIter],
1223+
(foo, bar) => [
1224+
() => {
1225+
runMyEffect(foo.value, bar.value, otherValue);
1226+
},
1227+
[foo.value, bar.value, otherValue],
1228+
]
1229+
);
1230+
1231+
// Or if returning an effect destructor function:
1232+
useAsyncIterEffect(
1233+
[fooIter, barIter],
1234+
(foo, bar) => [
1235+
() => {
1236+
runMyEffect(foo.value, bar.value, otherValue);
1237+
return () => {
1238+
cancelMyEffect();
1239+
}
1240+
},
1241+
[foo.value, bar.value, otherValue],
1242+
]
1243+
);
1244+
```
1245+
1246+
This hook is a consuming hook; any given item on the base deps array (first argument) that is async iterable will immediately start being iterated internally and continue for as long as its underlying iterable remains present in the array. Like most other hooks - plain (non async iterable) values can also be provided within the base deps at any time be conveyed as if are immediate, singular yields.
1247+
1248+
Whenever either of following events occur;
1249+
1250+
- Any of the base deps yields a value
1251+
- Hook is called again due to component re-render
1252+
1253+
-> the hook will call the effect resolver function (second argument) again, providing all the last states of the actively iterated items as individual arguments corresponding to their order within the base deps array. From there, you use it exactly like [`React.useEffect`](https://react.dev/reference/react/useEffect) while having the last yields accesible to use for your actual effect dependencies and/or your effect function's logic itself. The hook supports returning from the effect function an optional function to serve as the effect tear down/destructor, like the original [`React.useEffect`](https://react.dev/reference/react/useEffect).
1254+
1255+
### Parameters
1256+
1257+
- `baseDeps`:
1258+
An array of zero or more async iterable or plain values (mixable). In response to their yields, effect dependencies will re-evaluate and possibly fire the effect.
1259+
1260+
- `effectResolverFn`:
1261+
A user-provided function to be called by the hook whenever any yield occurres, getting the last states of all the actively iterated base deps as arguments. It should return a tuple with the effect function as the first item (_required_) and the next array of dependencies as the second (_optional_). The effect function may _optionally_ itself return a function to serve as a effect teardown/destructor.
1262+
1263+
1264+
### Returns
1265+
1266+
<ul>
1267+
1268+
_Nothing_
1269+
1270+
</ul>
1271+
1272+
### Notes
1273+
1274+
<ul>
1275+
1276+
> <br/>ℹ️ While you may optionally omit the dependency array in the effect resolver function's returned tuple as mentioned, note that this produces a similar behavior to calling [`React.useEffect`](https://react.dev/reference/react/useEffect) with dependencies omitted - the effect will be fired __on every re-render__ and __on every yield__ by the async iterable base dependencies.<br/><br/>
1277+
1278+
> <br/>ℹ️ It's important to remember that when using [`useAsyncIterEffect`](#useasyncitereffect) and [`<It>`](#it) to operate on the same async iterable, whether across components or within the same one - each of these two consumers would individually attempt to obtain an iterator from the same source iterable. Depending on how the source iterable's implementation it could lead to duplicate resources (e.g. WebSocket connections) being started. If this is undesirable, just ensure to pass that source iterable through a [`useSharedAsyncIter`](#usesharedasynciter) call anywhere along its route, ___before___ it encounters any consuming hooks like the latter ones.<br/><br/>
1279+
1280+
</ul>
1281+
1282+
1283+
11951284
### `useAsyncIterState`
11961285

11971286
Basically like [`React.useState`](https://react.dev/reference/react/useState), only that the value is provided back __wrapped in an async iterable__.

eslint.config.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ export default [
1818
...eslintTs.configs.recommended,
1919
eslintConfigPrettierTypeForced,
2020
{
21-
files: ['**/*.{ts,ts,tsx,js,mjs,jsx}'],
21+
ignores: ['dist/**/*'],
22+
},
23+
{
24+
files: ['**/*.{ts,mts,tsx,js,mjs,jsx}'],
2225
plugins: {},
2326
languageOptions: {
2427
globals: { ...globals.browser },

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-async-iterators",
3-
"version": "0.10.1",
3+
"version": "0.10.2",
44
"author": "Dor Shtaif <dorshtaif@gmail.com>",
55
"license": "MIT",
66
"description": "The magic of JavaScript async iterators in React ⛓️ 🧬 🔃",
@@ -46,9 +46,9 @@
4646
"test": "vitest --run --config ./spec/vitest.config.ts",
4747
"test:dev": "vitest --watch --config ./spec/vitest.config.ts",
4848
"test-typings-check": "tsc --noEmit -p ./spec/tsconfig.json",
49-
"build": "rm -rf ./dist && tsc -p tsconfig.json && tsc -p tsconfig-cjs.json && node ./scripts/set-module-type-in-dist-builds.mjs",
49+
"build": "rm -rf ./dist && tsc -p tsconfig.json && tsc -p tsconfig-cjs.json && tsx ./scripts/set-module-type-in-dist-builds.ts",
5050
"build-check": "tsc --noEmit -p ./tsconfig.json",
51-
"prepack": "npm run build"
51+
"prepack": "pnpm run build"
5252
},
5353
"peerDependencies": {
5454
"react": ">=17"
@@ -70,6 +70,7 @@
7070
"jsdom": "^25.0.1",
7171
"lodash-es": "^4.17.21",
7272
"prettier": "^3.4.2",
73+
"tsx": "^4.19.3",
7374
"typescript": "^5.7.2",
7475
"typescript-eslint": "^8.18.0",
7576
"vitest": "~3.0.5"

0 commit comments

Comments
 (0)