Skip to content

Commit 43112ec

Browse files
authored
refactor(useAsyncIterState): various code improvements (#50)
1 parent bfc1f09 commit 43112ec

File tree

3 files changed

+24
-22
lines changed

3 files changed

+24
-22
lines changed

src/common/callWithArgsOrReturn.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { type MaybeFunction } from './MaybeFunction.js';
2+
3+
export { callWithArgsOrReturn };
4+
5+
function callWithArgsOrReturn<T, TPassedArgs extends unknown[]>(
6+
value: MaybeFunction<T, TPassedArgs>,
7+
...argsToPass: TPassedArgs
8+
): T {
9+
return typeof value !== 'function'
10+
? value
11+
: (value as (...args: TPassedArgs) => T)(...argsToPass);
12+
}

src/useAsyncIterState/IterableChannel.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type MutableRefObject } from 'react';
22
import { promiseWithResolvers } from '../common/promiseWithResolvers.js';
3+
import { callWithArgsOrReturn } from '../common/callWithArgsOrReturn.js';
34

45
export { IterableChannel, type AsyncIterableSubject };
56

@@ -14,16 +15,8 @@ class IterableChannel<T, TInit = T> {
1415

1516
put(update: T | ((prevState: T | TInit) => T)): void {
1617
if (!this.#isClosed) {
17-
const value =
18-
typeof update !== 'function'
19-
? update
20-
: (() => {
21-
const updateFnTypePatched = update as (prevState: T | TInit) => T;
22-
return updateFnTypePatched(this.#currentValue);
23-
})();
24-
2518
(async () => {
26-
this.#currentValue = value;
19+
this.#currentValue = callWithArgsOrReturn(update, this.#currentValue);
2720
await undefined; // Deferring to the next microtick so that an attempt to pull the a value before making multiple rapid synchronous calls to `put()` will make that pull ultimately yield only the last value that was put - instead of the first one as were if this otherwise wasn't deferred.
2821
this.#nextIteration.resolve({ done: false, value: this.#currentValue });
2922
this.#nextIteration = promiseWithResolvers();

src/useAsyncIterState/index.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { useEffect, useRef } from 'react';
2-
import { IterableChannel, type AsyncIterableSubject } from './IterableChannel.js';
1+
import { useEffect } from 'react';
2+
import { callOrReturn } from '../common/callOrReturn.js';
3+
import { useRefWithInitialValue } from '../common/hooks/useRefWithInitialValue.js';
34
import { type Iterate } from '../Iterate/index.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
5+
import { IterableChannel, type AsyncIterableSubject } from './IterableChannel.js';
46

57
export { useAsyncIterState, type AsyncIterStateResult, type AsyncIterableSubject };
68

@@ -88,7 +90,7 @@ export { useAsyncIterState, type AsyncIterStateResult, type AsyncIterableSubject
8890
* @template TVal the type of state to be set and yielded by returned iterable.
8991
* @template TInitVal The type of the starting value for the state iterable's `.current.value` property.
9092
*
91-
* @param initialValue Any optional starting value for the state iterable's `.current.value` property, defaults to `undefined`.
93+
* @param initialValue Any optional starting value for the state iterable's `.current.value` 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).
9294
*
9395
* @returns a stateful async iterable and a function with which to yield an update, both maintain stable references across re-renders.
9496
*
@@ -107,22 +109,17 @@ function useAsyncIterState<TVal, TInitVal = undefined>(
107109
function useAsyncIterState<TVal, TInitVal>(
108110
initialValue?: TInitVal | (() => TInitVal)
109111
): AsyncIterStateResult<TVal, TInitVal> {
110-
const ref = useRef<{
112+
const ref = useRefWithInitialValue<{
111113
channel: IterableChannel<TVal, TInitVal>;
112114
result: AsyncIterStateResult<TVal, TInitVal>;
113-
}>();
114-
115-
ref.current ??= (() => {
116-
const initialValueDetermined =
117-
typeof initialValue !== 'function' ? initialValue : (initialValue as () => TInitVal)();
118-
119-
const channel = new IterableChannel<TVal, TInitVal>(initialValueDetermined as TInitVal);
120-
115+
}>(() => {
116+
const initialValueCalced = callOrReturn(initialValue) as TInitVal;
117+
const channel = new IterableChannel<TVal, TInitVal>(initialValueCalced);
121118
return {
122119
channel,
123120
result: [channel.values, newVal => channel.put(newVal)],
124121
};
125-
})();
122+
});
126123

127124
const { channel, result } = ref.current;
128125

0 commit comments

Comments
 (0)