@@ -14,7 +14,7 @@ import type { FormContextProps } from './FormContext';
14
14
import FormContext from './FormContext' ;
15
15
import { isSimilar } from './utils/valueUtil' ;
16
16
import ListContext from './ListContext' ;
17
- import BatchUpdate , { BatchUpdateRef } from './BatchUpdate' ;
17
+ import BatchUpdate , { BatchTask , type BatchUpdateRef } from './BatchUpdate' ;
18
18
19
19
type BaseFormProps = Omit < React . FormHTMLAttributes < HTMLFormElement > , 'onSubmit' | 'children' > ;
20
20
@@ -61,8 +61,6 @@ const Form: React.ForwardRefRenderFunction<FormRef, FormProps> = (
61
61
const nativeElementRef = React . useRef < HTMLFormElement > ( null ) ;
62
62
const formContext : FormContextProps = React . useContext ( FormContext ) ;
63
63
64
- const batchUpdateRef = React . useRef < BatchUpdateRef > ( null ) ;
65
-
66
64
// We customize handle event since Context will makes all the consumer re-render:
67
65
// https://reactjs.org/docs/context.html#contextprovider
68
66
const [ formInstance ] = useForm ( form ) ;
@@ -122,9 +120,42 @@ const Form: React.ForwardRefRenderFunction<FormRef, FormProps> = (
122
120
mountRef . current = true ;
123
121
}
124
122
123
+ // ======================== Batch Update ========================
124
+ // zombieJ:
125
+ // To avoid Form self re-render,
126
+ // We create a sub component `BatchUpdate` to handle batch update logic.
127
+ // When the call with do not change immediate, we will batch the update
128
+ // and flush it in `useLayoutEffect` for next tick.
129
+
125
130
// Set batch update ref
126
- setBatchUpdate ( batchUpdateRef ) ;
131
+ const batchUpdateRef = React . useRef < BatchUpdateRef > ( null ) ;
132
+ const batchUpdateTasksRef = React . useRef < [ key : string , fn : VoidFunction ] [ ] > ( [ ] ) ;
133
+
134
+ const tryFlushBatch = ( ) => {
135
+ if ( batchUpdateRef . current ) {
136
+ batchUpdateTasksRef . current . forEach ( ( [ key , fn ] ) => {
137
+ batchUpdateRef . current . batch ( key , fn ) ;
138
+ } ) ;
139
+ batchUpdateTasksRef . current = [ ] ;
140
+ }
141
+ } ;
142
+
143
+ // Ref update
144
+ const setBatchUpdateRef = React . useCallback ( ( batchUpdate : BatchUpdateRef | null ) => {
145
+ batchUpdateRef . current = batchUpdate ;
146
+ tryFlushBatch ( ) ;
147
+ } , [ ] ) ;
148
+
149
+ // Task list
150
+
151
+ const batchUpdate : BatchTask = ( key , callback ) => {
152
+ batchUpdateTasksRef . current . push ( [ key , callback ] ) ;
153
+ tryFlushBatch ( ) ;
154
+ } ;
155
+
156
+ setBatchUpdate ( batchUpdate ) ;
127
157
158
+ // ========================== Unmount ===========================
128
159
React . useEffect (
129
160
( ) => ( ) => destroyForm ( clearOnDestroy ) ,
130
161
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -153,6 +184,7 @@ const Form: React.ForwardRefRenderFunction<FormRef, FormProps> = (
153
184
prevFieldsRef . current = fields ;
154
185
} , [ fields , formInstance ] ) ;
155
186
187
+ // =========================== Render ===========================
156
188
const formContextValue = React . useMemo (
157
189
( ) => ( {
158
190
...( formInstance as InternalFormInstance ) ,
@@ -164,7 +196,7 @@ const Form: React.ForwardRefRenderFunction<FormRef, FormProps> = (
164
196
const wrapperNode = (
165
197
< ListContext . Provider value = { null } >
166
198
< FieldContext . Provider value = { formContextValue } > { childrenNode } </ FieldContext . Provider >
167
- < BatchUpdate ref = { batchUpdateRef } />
199
+ < BatchUpdate ref = { setBatchUpdateRef } />
168
200
</ ListContext . Provider >
169
201
) ;
170
202
0 commit comments