@@ -14,7 +14,7 @@ import type { FormContextProps } from './FormContext';
1414import FormContext from './FormContext' ;
1515import { isSimilar } from './utils/valueUtil' ;
1616import ListContext from './ListContext' ;
17- import BatchUpdate , { BatchUpdateRef } from './BatchUpdate' ;
17+ import BatchUpdate , { BatchTask , type BatchUpdateRef } from './BatchUpdate' ;
1818
1919type BaseFormProps = Omit < React . FormHTMLAttributes < HTMLFormElement > , 'onSubmit' | 'children' > ;
2020
@@ -61,8 +61,6 @@ const Form: React.ForwardRefRenderFunction<FormRef, FormProps> = (
6161 const nativeElementRef = React . useRef < HTMLFormElement > ( null ) ;
6262 const formContext : FormContextProps = React . useContext ( FormContext ) ;
6363
64- const batchUpdateRef = React . useRef < BatchUpdateRef > ( null ) ;
65-
6664 // We customize handle event since Context will makes all the consumer re-render:
6765 // https://reactjs.org/docs/context.html#contextprovider
6866 const [ formInstance ] = useForm ( form ) ;
@@ -122,9 +120,42 @@ const Form: React.ForwardRefRenderFunction<FormRef, FormProps> = (
122120 mountRef . current = true ;
123121 }
124122
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+
125130 // 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 ) ;
127157
158+ // ========================== Unmount ===========================
128159 React . useEffect (
129160 ( ) => ( ) => destroyForm ( clearOnDestroy ) ,
130161 // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -153,6 +184,7 @@ const Form: React.ForwardRefRenderFunction<FormRef, FormProps> = (
153184 prevFieldsRef . current = fields ;
154185 } , [ fields , formInstance ] ) ;
155186
187+ // =========================== Render ===========================
156188 const formContextValue = React . useMemo (
157189 ( ) => ( {
158190 ...( formInstance as InternalFormInstance ) ,
@@ -164,7 +196,7 @@ const Form: React.ForwardRefRenderFunction<FormRef, FormProps> = (
164196 const wrapperNode = (
165197 < ListContext . Provider value = { null } >
166198 < FieldContext . Provider value = { formContextValue } > { childrenNode } </ FieldContext . Provider >
167- < BatchUpdate ref = { batchUpdateRef } />
199+ < BatchUpdate ref = { setBatchUpdateRef } />
168200 </ ListContext . Provider >
169201 ) ;
170202
0 commit comments