Skip to content

Commit 45ae169

Browse files
committed
feat: implement batching algorithm and integrate it
1 parent a1f6501 commit 45ae169

File tree

3 files changed

+46
-14
lines changed

3 files changed

+46
-14
lines changed

packages/react/src/core/useEffect.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { subscribeToStateChange } from "./useState";
1+
import { flushStateUpdates, subscribeToStateChange } from "./useState";
22
import { compareArrays, getCallerStack } from "./utils";
33
import componentRegistry from "./componentRegistry";
44

@@ -23,7 +23,8 @@ const effectsStore: Record<string, StoreItemType> = {};
2323
const subscribeToComponentRegistryComplete = (callback: Function) => {
2424
return componentRegistry.subscribeToStateChange((state) => {
2525
if (state === "completed") {
26-
return callback();
26+
callback();
27+
flushStateUpdates();
2728
}
2829
});
2930
};
@@ -45,6 +46,7 @@ const useEffect = (
4546
],
4647
};
4748

49+
// refresh the cleanup function
4850
subscribeToComponentRegistryComplete(() => {
4951
effectsStore[callerStack].effects[0].cleanupFunction = callback();
5052
});

packages/react/src/core/useState.ts

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { getCallerStack } from "./utils";
55

66
type StateType = {
77
cursor: number;
8-
values: any[];
8+
values: {
9+
value: any;
10+
updateQueue: any[];
11+
}[];
912
};
1013
type StateSubscriberType = () => void;
1114

@@ -29,19 +32,26 @@ function useState<T>(
2932
initialValue instanceof Function ? initialValue() : initialValue;
3033
states[stringCallerStack] = {
3134
cursor: 0,
32-
values: [savedValue],
35+
values: [
36+
{
37+
updateQueue: [],
38+
value: savedValue,
39+
},
40+
],
3341
};
3442
}
3543

3644
const currentCursor = states[stringCallerStack].cursor;
3745
const currentValues = states[stringCallerStack].values;
46+
const currentUpdateQueue =
47+
states[stringCallerStack].values[currentCursor].updateQueue;
3848

39-
if (currentValues[currentCursor] === undefined) {
40-
currentValues[currentCursor] = initialValue;
49+
if (currentValues[currentCursor].value === undefined) {
50+
currentValues[currentCursor].value = initialValue;
4151
}
4252

4353
const performUpdate: UpdaterFunctionType<T> = (newValue) => {
44-
if (Object.is(currentValues[currentCursor], newValue)) {
54+
if (Object.is(currentValues[currentCursor].value, newValue)) {
4555
return;
4656
}
4757

@@ -54,17 +64,17 @@ function useState<T>(
5464
subscriber();
5565
}
5666

57-
currentValues[currentCursor] =
58-
newValue instanceof Function
59-
? newValue(currentValues[currentCursor])
60-
: newValue;
61-
62-
ReactDOM.render();
67+
// for batching
68+
const latestValue =
69+
currentUpdateQueue.at(-1) || currentValues[currentCursor].value;
70+
const queueItem =
71+
newValue instanceof Function ? newValue(latestValue) : newValue;
72+
currentUpdateQueue.push(queueItem);
6373
};
6474

6575
states[stringCallerStack].cursor++;
6676

67-
return [currentValues[currentCursor], performUpdate];
77+
return [currentValues[currentCursor].value, performUpdate];
6878
}
6979

7080
componentRegistry.subscribeToStoreChange(
@@ -84,4 +94,20 @@ export function subscribeToStateChange(callback: StateSubscriberType) {
8494
stateSubscribers.push(callback);
8595
}
8696

97+
export function flushStateUpdates() {
98+
for (const key in states) {
99+
for (let i = 0; i < states[key].values.length; i++) {
100+
const state = states[key].values[i];
101+
// prevent from falsy undefined assignment
102+
if (state.updateQueue.length === 0) {
103+
continue;
104+
}
105+
106+
state.value = state.updateQueue.at(-1);
107+
state.updateQueue = [];
108+
}
109+
}
110+
ReactDOM.render();
111+
}
112+
87113
export default useState;

packages/react/src/dom/eventRegistry.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { flushStateUpdates } from "../core/useState";
2+
13
type EventType = string;
24
type EventTargetType = HTMLElement | SVGElement;
35
type EventHandlerType = (event: Event) => void;
@@ -27,6 +29,7 @@ class EventRegistry {
2729
const eventHandlerWrapper = (e: Event) => {
2830
if (elementAttachableEvents.includes(event)) {
2931
eventHandler(e);
32+
flushStateUpdates();
3033
return;
3134
}
3235

@@ -35,6 +38,7 @@ class EventRegistry {
3538
}
3639

3740
eventHandler(e);
41+
flushStateUpdates();
3842
};
3943

4044
if (this.hasEvent(event)) {

0 commit comments

Comments
 (0)