@@ -4,13 +4,13 @@ import FieldContext, { HOOK_MARK } from './FieldContext';
44import type {
55 FormInstance ,
66 InternalFormInstance ,
7- InternalNamePath ,
87 NamePath ,
98 Store ,
109 WatchOptions ,
1110} from './interface' ;
1211import { isFormInstance } from './utils/typeUtil' ;
1312import { getNamePath , getValue } from './utils/valueUtil' ;
13+ import { useEvent } from '@rc-component/util' ;
1414
1515type ReturnPromise < T > = T extends Promise < infer ValueType > ? ValueType : never ;
1616type GetGeneric < TForm extends FormInstance > = ReturnPromise < ReturnType < TForm [ 'validateFields' ] > > ;
@@ -23,19 +23,6 @@ export function stringify(value: any) {
2323 }
2424}
2525
26- const useWatchWarning =
27- process . env . NODE_ENV !== 'production'
28- ? ( namePath : InternalNamePath ) => {
29- const fullyStr = namePath . join ( '__RC_FIELD_FORM_SPLIT__' ) ;
30- const nameStrRef = useRef ( fullyStr ) ;
31-
32- warning (
33- nameStrRef . current === fullyStr ,
34- '`useWatch` is not support dynamic `namePath`. Please provide static instead.' ,
35- ) ;
36- }
37- : ( ) => { } ;
38-
3926function useWatch <
4027 TDependencies1 extends keyof GetGeneric < TForm > ,
4128 TForm extends FormInstance ,
@@ -123,56 +110,57 @@ function useWatch(
123110 ) ;
124111 }
125112
126- const namePath = getNamePath ( dependencies ) ;
127- const namePathRef = useRef ( namePath ) ;
128- namePathRef . current = namePath ;
129-
130- useWatchWarning ( namePath ) ;
131-
132- useEffect (
133- ( ) => {
134- // Skip if not exist form instance
135- if ( ! isValidForm ) {
136- return ;
137- }
138-
139- const { getFieldsValue, getInternalHooks } = formInstance ;
140- const { registerWatch } = getInternalHooks ( HOOK_MARK ) ;
141-
142- const getWatchValue = ( values : any , allValues : any ) => {
143- const watchValue = options . preserve ? allValues : values ;
144- return typeof dependencies === 'function'
145- ? dependencies ( watchValue )
146- : getValue ( watchValue , namePathRef . current ) ;
147- } ;
148-
149- const cancelRegister = registerWatch ( ( values , allValues ) => {
150- const newValue = getWatchValue ( values , allValues ) ;
151- const nextValueStr = stringify ( newValue ) ;
152-
153- // Compare stringify in case it's nest object
154- if ( valueStrRef . current !== nextValueStr ) {
155- valueStrRef . current = nextValueStr ;
156- setValue ( newValue ) ;
157- }
158- } ) ;
159-
160- // TODO: We can improve this perf in future
161- const initialValue = getWatchValue ( getFieldsValue ( ) , getFieldsValue ( true ) ) ;
162-
163- // React 18 has the bug that will queue update twice even the value is not changed
164- // ref: https://github.com/facebook/react/issues/27213
165- if ( value !== initialValue ) {
166- setValue ( initialValue ) ;
167- }
168-
169- return cancelRegister ;
170- } ,
171-
172- // We do not need re-register since namePath content is the same
113+ // ============================== Form ==============================
114+ const { getFieldsValue, getInternalHooks } = formInstance ;
115+ const { registerWatch } = getInternalHooks ( HOOK_MARK ) ;
116+
117+ // ============================= Update =============================
118+ const triggerUpdate = useEvent ( ( values ?: any , allValues ?: any ) => {
119+ const watchValue = options . preserve
120+ ? ( allValues ?? getFieldsValue ( true ) )
121+ : ( values ?? getFieldsValue ( ) ) ;
122+
123+ const nextValue =
124+ typeof dependencies === 'function'
125+ ? dependencies ( watchValue )
126+ : getValue ( watchValue , getNamePath ( dependencies ) ) ;
127+
128+ if ( stringify ( value ) !== stringify ( nextValue ) ) {
129+ setValue ( nextValue ) ;
130+ }
131+ } ) ;
132+
133+ // ============================= Effect =============================
134+ const flattenDeps =
135+ typeof dependencies === 'function' ? dependencies : JSON . stringify ( dependencies ) ;
136+
137+ // Deps changed
138+ useEffect ( ( ) => {
139+ // Skip if not exist form instance
140+ if ( ! isValidForm ) {
141+ return ;
142+ }
143+
144+ triggerUpdate ( ) ;
145+
146+ // eslint-disable-next-line react-hooks/exhaustive-deps
147+ } , [ isValidForm , flattenDeps ] ) ;
148+
149+ // Value changed
150+ useEffect ( ( ) => {
151+ // Skip if not exist form instance
152+ if ( ! isValidForm ) {
153+ return ;
154+ }
155+
156+ const cancelRegister = registerWatch ( ( values , allValues ) => {
157+ triggerUpdate ( values , allValues ) ;
158+ } ) ;
159+
160+ return cancelRegister ;
161+
173162 // eslint-disable-next-line react-hooks/exhaustive-deps
174- [ isValidForm ] ,
175- ) ;
163+ } , [ isValidForm ] ) ;
176164
177165 return value ;
178166}
0 commit comments