-
Notifications
You must be signed in to change notification settings - Fork 44
/
common.js
155 lines (144 loc) · 4.29 KB
/
common.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import React, {
useDeferredValue,
useEffect,
useState,
useTransition,
useRef,
} from 'react';
// block for about 20 ms
const syncBlock = () => {
const start = performance.now();
while (performance.now() - start < 20) {
// empty
}
};
export const initialState = {
count: 0,
};
export const reducer = (state = initialState, action = {}) => {
switch (action.type) {
case 'increment':
return {
...state,
count: state.count + 1,
};
case 'double':
return {
...state,
count: state.count * 2,
};
default:
return state;
}
};
export const selectCount = (state) => state.count;
export const incrementAction = { type: 'increment' };
export const doubleAction = { type: 'double' };
export const NUM_CHILD_COMPONENTS = 50;
const ids = [...Array(NUM_CHILD_COMPONENTS).keys()];
// check if all child components show the same count
// and if not, change the title
export const useCheckTearing = () => {
useEffect(() => {
try {
const counts = ids.map((i) => Number(
document.querySelector(`.count:nth-of-type(${i + 1})`).innerHTML,
));
counts.push(Number(document.getElementById('mainCount').innerHTML));
if (!counts.every((c) => c === counts[0])) {
console.error('count mismatch', counts);
document.title += ' TEARED';
}
} catch (e) {
// ignored
}
});
};
export const createApp = (
useCount,
useIncrement,
useDouble,
Root = React.Fragment,
componentWrapper = React.memo,
mainWrapper = (fn) => fn,
) => {
const Counter = componentWrapper(() => {
const count = useCount();
syncBlock();
return <div className="count">{count}</div>;
});
const DeferredCounter = componentWrapper(() => {
const count = useDeferredValue(useCount());
syncBlock();
return <div className="count">{count}</div>;
});
const Main = mainWrapper(() => {
const [isPending, startTransition] = useTransition();
const [mode, setMode] = useState(null);
const transitionHide = () => {
startTransition(() => setMode(null));
};
const transitionShowCounter = () => {
startTransition(() => setMode('counter'));
};
const transitionShowDeferred = () => {
startTransition(() => setMode('deferred'));
};
const count = useCount();
const deferredCount = useDeferredValue(count);
useCheckTearing();
const increment = useIncrement();
const doDouble = useDouble();
const transitionIncrement = () => {
startTransition(increment);
};
const timer = useRef();
const stopAutoIncrement = () => {
clearInterval(timer.current);
};
const startAutoIncrement = () => {
stopAutoIncrement();
timer.current = setInterval(increment, 50);
};
return (
<div>
<button type="button" id="transitionHide" onClick={transitionHide}>
Hide in transition
</button>
<button type="button" id="transitionShowCounter" onClick={transitionShowCounter}>
Show counter in transition
</button>
<button type="button" id="transitionShowDeferred" onClick={transitionShowDeferred}>
Show deferred counter in transition
</button>
<button type="button" id="normalIncrement" onClick={increment}>
Increment count normally
</button>
<button type="button" id="normalDouble" onClick={doDouble}>
Double count normally
</button>
<button type="button" id="transitionIncrement" onClick={transitionIncrement}>
Increment count in transition
</button>
<button type="button" id="stopAutoIncrement" onClick={stopAutoIncrement}>
Stop auto incrementing count
</button>
<button type="button" id="startAutoIncrement" onClick={startAutoIncrement}>
Start auto incrementing count
</button>
<span id="pending">{isPending && 'Pending...'}</span>
<h1>Counters</h1>
{mode === 'counter' && ids.map((id) => <Counter key={id} />)}
{mode === 'deferred' && ids.map((id) => <DeferredCounter key={id} />)}
<h1>Main</h1>
<div id="mainCount" className="count">{mode === 'deferred' ? deferredCount : count}</div>
</div>
);
});
const App = () => (
<Root>
<Main />
</Root>
);
return App;
};