Skip to content

Commit 150370d

Browse files
committed
fix: update state definitions
1 parent e9b0e5e commit 150370d

File tree

1 file changed

+14
-11
lines changed

1 file changed

+14
-11
lines changed

site/src/components/Spinner/Spinner.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,35 +128,38 @@ export const Spinner: FC<SpinnerProps> = ({
128128

129129
// Not a big fan of one-time custom hooks, but it helps insulate the main
130130
// component from the chaos of handling all these state syncs, when the ultimate
131-
// result is a simple boolean. V8 will be able to inline the function definition
132-
// in some cases anyway
131+
// result is a simple boolean. A lot of browsers will be able to inline the
132+
// function definition in some cases anyway
133133
function useShowSpinner(
134134
loading: boolean,
135135
spinnerStartDelayMs: number,
136136
): boolean {
137137
// Doing a bunch of mid-render state syncs to minimize risks of
138138
// contradictory states during re-renders. It's ugly, but it's what the
139-
// React team officially recommends
140-
const noDelay = spinnerStartDelayMs === 0;
141-
const [mountSpinner, setMountSpinner] = useState(noDelay);
142-
const unmountImmediatelyOnRerender = mountSpinner && !loading;
139+
// React team officially recommends. Be very careful with this logic; React
140+
// only bails out of redundant state updates if they happen outside of a
141+
// render. Inside a render, if you keep calling a state dispatch, you will
142+
// get an infinite render loop, no matter what the state value is.
143+
const [spinnerActive, setSpinnerActive] = useState(spinnerStartDelayMs === 0);
144+
const unmountImmediatelyOnRerender = spinnerActive && !loading;
143145
if (unmountImmediatelyOnRerender) {
144-
setMountSpinner(false);
146+
setSpinnerActive(false);
145147
}
146-
const mountImmediatelyOnRerender = !mountSpinner && noDelay;
148+
const mountImmediatelyOnRerender =
149+
!spinnerActive && loading && spinnerStartDelayMs === 0;
147150
if (mountImmediatelyOnRerender) {
148-
setMountSpinner(true);
151+
setSpinnerActive(true);
149152
}
150153
useEffect(() => {
151154
if (spinnerStartDelayMs === 0) {
152155
return;
153156
}
154157

155158
const delayId = window.setTimeout(() => {
156-
setMountSpinner(true);
159+
setSpinnerActive(true);
157160
}, spinnerStartDelayMs);
158161
return () => window.clearTimeout(delayId);
159162
}, [spinnerStartDelayMs]);
160163

161-
return loading && mountSpinner;
164+
return spinnerActive && loading;
162165
}

0 commit comments

Comments
 (0)