@@ -31,8 +31,8 @@ type SpinnerProps = Readonly<
31
31
loading : boolean ;
32
32
33
33
/**
34
- * Indicates whether the children prop should be unmounted during
35
- * a loading state. Defaults to false - unmounting HTML elements
34
+ * Indicates whether the ` children` prop should be unmounted during
35
+ * a loading state. Defaults to ` false` - unmounting HTML elements
36
36
* like form controls can lead to invalid HTML, so this prop should
37
37
* be used with care and only if it prevents render performance
38
38
* issues.
@@ -42,17 +42,22 @@ type SpinnerProps = Readonly<
42
42
/**
43
43
* Specifies whether there should be a delay before the spinner
44
44
* appears on screen. If not specified, the spinner always appears
45
- * immediately.
45
+ * immediately when `loading` is true .
46
46
*
47
- * Can help avoid page flickering issues. (e.g., You have a modal
48
- * that takes a moment to close, and it has Spinner content inside
49
- * it. The user triggers a loading transition, and you want to show
50
- * the spinner at some point if a transition takes long enough, but
51
- * if the spinner mounting and modal closing happen in too quick of
52
- * a succession, the UI looks janky. So even though you might flip
53
- * the loading state immediately, you want to wait a second to show
54
- * it in case the modal can close quickly enough. It's lying to the
55
- * user in a way that makes the UI feel more polished.)
47
+ * Can help avoid page flickering issues. Example:
48
+ * 1. You have a modal that takes a moment to close, and it has
49
+ * Spinner content inside it.
50
+ * 2. The user triggers a loading transition, and you want to show
51
+ * the spinner at some point, especially if the transition takes
52
+ * long enough.
53
+ * 3. The problem is, if the spinner mounting and modal closing
54
+ * happen in quick enough succession, the user will see a brief
55
+ * spinner flicker before the modal closes, making the UI appear
56
+ * janky, even though it is accurately modeling the state of the
57
+ * application.
58
+ * 4. The solution, then, to keep the UI feeling polished, is to lie
59
+ * to the user and only show the loading state if some
60
+ * pre-determined period has lapsed.
56
61
*/
57
62
spinnerDelayMs ?: number ;
58
63
}
@@ -128,8 +133,9 @@ export const Spinner: FC<SpinnerProps> = ({
128
133
} ;
129
134
130
135
// Splitting off logic into custom hook so that we can abstract away the chaos
131
- // of handling Spinner's re-render logic. Not worried about overhead; a lot of
132
- // browsers should be able to inline this function call
136
+ // of handling Spinner's re-render logic. The result is a simple boolean, but
137
+ // the steps to calculate that boolean accurately while avoiding re-render
138
+ // issues got a little heady
133
139
function useShowSpinner ( loading : boolean , spinnerDelayMs : number ) : boolean {
134
140
// Disallow negative timeout values and fractional values, but also round
135
141
// the delay down if it's small enough that it might as well be immediate
@@ -147,13 +153,13 @@ function useShowSpinner(loading: boolean, spinnerDelayMs: number): boolean {
147
153
// updates if they happen outside of a render. Inside a render, if you keep
148
154
// calling a state dispatch, you will get an infinite render loop, no matter
149
155
// what the value you dispatch. There must be a "base case" in the render
150
- // path that causes state dispatches to stop eventually so that React can
151
- // move on to the next component in the tree
156
+ // path that causes state dispatches to stop entirely so that React can
157
+ // move on to the next component in the Fiber subtree
152
158
const [ delayLapsed , setDelayLapsed ] = useState ( safeDelay === 0 ) ;
153
159
const [ renderCache , setRenderCache ] = useState ( { loading, safeDelay } ) ;
154
- const resetOnRerender =
160
+ const canResetLapseOnRerender =
155
161
delayLapsed && ! loading && loading !== renderCache . loading ;
156
- if ( resetOnRerender ) {
162
+ if ( canResetLapseOnRerender ) {
157
163
setDelayLapsed ( false ) ;
158
164
setRenderCache ( ( current ) => ( { ...current , loading } ) ) ;
159
165
}
0 commit comments