Skip to content

Commit 6dd5ac5

Browse files
authored
fix(useAsyncIterState): disallow user manipulation of the state iterable's current value property (#43)
1 parent 675331f commit 6dd5ac5

File tree

3 files changed

+20
-5
lines changed

3 files changed

+20
-5
lines changed

eslint.config.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export default [
4343
['@typescript-eslint/no-explicit-any']: 'off',
4444
['@typescript-eslint/no-non-null-assertion']: 'off',
4545
['@typescript-eslint/no-empty-function']: 'off',
46+
['@typescript-eslint/no-this-alias']: 'off',
4647
['@typescript-eslint/no-unused-expressions']: 'warn',
4748
['@typescript-eslint/no-unused-vars']: [
4849
'warn',

spec/tests/useAsyncIterState.spec.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,12 @@ describe('`useAsyncIterState` hook', () => {
153153
expect(currentValues).toStrictEqual([undefined, 'a', 'b', 'c']);
154154
}
155155
);
156+
157+
it(gray("The state iterable's `.current.value` property is read-only"), async () => {
158+
const [values] = renderHook(() => useAsyncIterState<number>()).result.current;
159+
160+
expect(() => {
161+
(values.value as any).current = "can't do this...";
162+
}).toThrow(TypeError);
163+
});
156164
});

src/useAsyncIterState/IterableChannel.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ export { IterableChannel, type AsyncIterableSubject };
66
class IterableChannel<T> {
77
#isClosed = false;
88
#nextIteration = promiseWithResolvers<IteratorResult<T, void>>();
9+
#currentValue: T | undefined;
910

1011
put(value: T): void {
1112
if (!this.#isClosed) {
12-
this.values.value.current = value;
13+
this.#currentValue = value;
1314
this.#nextIteration.resolve({ done: false, value });
1415
this.#nextIteration = promiseWithResolvers();
1516
}
@@ -21,9 +22,14 @@ class IterableChannel<T> {
2122
}
2223

2324
values: AsyncIterableSubject<T> = {
24-
value: {
25-
current: undefined,
26-
},
25+
value: (() => {
26+
const self = this;
27+
return {
28+
get current() {
29+
return self.#currentValue;
30+
},
31+
};
32+
})(),
2733

2834
[Symbol.asyncIterator]: () => {
2935
const whenIteratorClosed = promiseWithResolvers<IteratorReturnResult<undefined>>();
@@ -52,7 +58,7 @@ type AsyncIterableSubject<T> = {
5258
/**
5359
* A React Ref-like object whose inner `current` property shows the most up to date state value.
5460
*/
55-
value: MutableRefObject<T | undefined>;
61+
value: Readonly<MutableRefObject<T | undefined>>;
5662

5763
/**
5864
* Returns an async iterator to iterate over. All iterators returned by this share the same source

0 commit comments

Comments
 (0)