Skip to content

Commit 16e37bb

Browse files
committed
fix yielding consecutive identical values causes unnecessary re-renders for useAsyncIter and <Iterate> unlink React.useState
1 parent ebcbfd4 commit 16e37bb

File tree

3 files changed

+72
-7
lines changed

3 files changed

+72
-7
lines changed

spec/tests/Iterate.spec.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,42 @@ describe('`Iterate` component', () => {
667667
);
668668
}
669669
);
670+
671+
it(
672+
gray(
673+
'When given iterable yields consecutive identical values the hook will not consequently re-render'
674+
),
675+
async () => {
676+
let timesRerendered = 0;
677+
let lastRenderFnInput: undefined | IterationResult<string>;
678+
const channel = new IterableChannelTestHelper<string>();
679+
680+
const rendered = render(
681+
<Iterate value={channel}>
682+
{next => {
683+
timesRerendered++;
684+
lastRenderFnInput = next;
685+
return <div id="test-created-elem">Render count: {timesRerendered}</div>;
686+
}}
687+
</Iterate>
688+
);
689+
690+
for (let i = 0; i < 3; ++i) {
691+
await act(() => channel.put('a'));
692+
}
693+
694+
expect(timesRerendered).toStrictEqual(2);
695+
expect(lastRenderFnInput).toStrictEqual({
696+
value: 'a',
697+
pendingFirst: false,
698+
done: false,
699+
error: undefined,
700+
});
701+
expect(rendered.container.innerHTML).toStrictEqual(
702+
'<div id="test-created-elem">Render count: 2</div>'
703+
);
704+
}
705+
);
670706
});
671707

672708
const simulatedError = new Error('🚨 Simulated Error 🚨');

spec/tests/useAsyncIter.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,33 @@ describe('`useAsyncIter` hook', () => {
471471
});
472472
}
473473
);
474+
475+
it(
476+
gray(
477+
'When given iterable yields consecutive identical values the hook will not consequently re-render'
478+
),
479+
async () => {
480+
let timesRerendered = 0;
481+
const channel = new IterableChannelTestHelper<string>();
482+
483+
const renderedHook = renderHook(() => {
484+
timesRerendered++;
485+
return useAsyncIter(channel);
486+
});
487+
488+
for (let i = 0; i < 3; ++i) {
489+
await act(() => channel.put('a'));
490+
}
491+
492+
expect(timesRerendered).toStrictEqual(2);
493+
expect(renderedHook.result.current).toStrictEqual({
494+
value: 'a',
495+
pendingFirst: false,
496+
done: false,
497+
error: undefined,
498+
});
499+
}
500+
);
474501
});
475502

476503
const simulatedError = new Error('🚨 Simulated Error 🚨');

src/useAsyncIter/index.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,15 @@ const useAsyncIter: {
169169
iterationIdx++
170170
) ?? (value as ExtractAsyncIterValue<TVal>);
171171

172-
stateRef.current = {
173-
value: formattedValue,
174-
pendingFirst: false,
175-
done: false,
176-
error: undefined,
177-
};
178-
rerender();
172+
if (!Object.is(formattedValue, stateRef.current.value)) {
173+
stateRef.current = {
174+
value: formattedValue,
175+
pendingFirst: false,
176+
done: false,
177+
error: undefined,
178+
};
179+
rerender();
180+
}
179181
}
180182
}
181183
if (!iteratorClosedByConsumer) {

0 commit comments

Comments
 (0)