@@ -7,10 +7,12 @@ import type {
77 InternalNamePath ,
88 NamePath ,
99 Store ,
10+ WatchCallBack ,
1011 WatchOptions ,
1112} from './interface' ;
1213import { isFormInstance } from './utils/typeUtil' ;
1314import { getNamePath , getValue } from './utils/valueUtil' ;
15+ import { useEvent } from '@rc-component/util' ;
1416
1517type ReturnPromise < T > = T extends Promise < infer ValueType > ? ValueType : never ;
1618type GetGeneric < TForm extends FormInstance > = ReturnPromise < ReturnType < TForm [ 'validateFields' ] > > ;
@@ -23,19 +25,6 @@ export function stringify(value: any) {
2325 }
2426}
2527
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-
3928function useWatch <
4029 TDependencies1 extends keyof GetGeneric < TForm > ,
4130 TForm extends FormInstance ,
@@ -123,56 +112,57 @@ function useWatch(
123112 ) ;
124113 }
125114
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
115+ // ============================== Form ==============================
116+ const { getFieldsValue, getInternalHooks } = formInstance ;
117+ const { registerWatch } = getInternalHooks ( HOOK_MARK ) ;
118+
119+ // ============================= Update =============================
120+ const triggerUpdate = useEvent ( ( values ?: any , allValues ?: any ) => {
121+ const watchValue = options . preserve
122+ ? ( allValues ?? getFieldsValue ( true ) )
123+ : ( values ?? getFieldsValue ( ) ) ;
124+
125+ const nextValue =
126+ typeof dependencies === 'function'
127+ ? dependencies ( watchValue )
128+ : getValue ( watchValue , getNamePath ( dependencies ) ) ;
129+
130+ if ( stringify ( value ) !== stringify ( nextValue ) ) {
131+ setValue ( nextValue ) ;
132+ }
133+ } ) ;
134+
135+ // ============================= Effect =============================
136+ const flattenDeps =
137+ typeof dependencies === 'function' ? dependencies : JSON . stringify ( dependencies ) ;
138+
139+ // Deps changed
140+ useEffect ( ( ) => {
141+ // Skip if not exist form instance
142+ if ( ! isValidForm ) {
143+ return ;
144+ }
145+
146+ triggerUpdate ( ) ;
147+
148+ // eslint-disable-next-line react-hooks/exhaustive-deps
149+ } , [ isValidForm , flattenDeps ] ) ;
150+
151+ // Value changed
152+ useEffect ( ( ) => {
153+ // Skip if not exist form instance
154+ if ( ! isValidForm ) {
155+ return ;
156+ }
157+
158+ const cancelRegister = registerWatch ( ( values , allValues ) => {
159+ triggerUpdate ( values , allValues ) ;
160+ } ) ;
161+
162+ return cancelRegister ;
163+
173164 // eslint-disable-next-line react-hooks/exhaustive-deps
174- [ isValidForm ] ,
175- ) ;
165+ } , [ isValidForm ] ) ;
176166
177167 return value ;
178168}
0 commit comments