11'use client' ;
22/* eslint-disable no-bitwise */
33
4- import { createContext , useContext , useEffect , useRef , useState } from 'react' ;
4+ import { createContext , useContext , useEffect , useState } from 'react' ;
55import { flushSync } from 'react-dom' ;
66import type { AllPaths , PathToDeepType , ShapeFromPaths } from 'skyroc-type-utils' ;
77
88import type { ChangeMask , SubscribeMaskOptions } from './form-core/event' ;
9- import { ChangeTag , toMask } from './form-core/event' ;
9+ import { toMask } from './form-core/event' ;
1010import type { Action , Middleware } from './form-core/middleware' ;
1111import type { FieldEntity } from './form-core/types' ;
1212import type { ValidateMessages } from './form-core/validate' ;
13- import type { Rule } from './form-core/validation' ;
13+ import type { Rule , ValidateOptions } from './form-core/validation' ;
1414import type { FormState } from './types' ;
1515import type { Meta } from './types/shared-types' ;
1616
@@ -24,6 +24,7 @@ export interface ValuesOptions<Values = any> {
2424export interface StateOptions < Values = any > {
2525 getField : < T extends AllPaths < Values > > ( name : T ) => Meta < T , PathToDeepType < Values , T > > ;
2626 getFieldError : ( name : AllPaths < Values > ) => string [ ] ;
27+ getFields : ( names ?: AllPaths < Values > [ ] ) => Meta < AllPaths < Values > , PathToDeepType < Values , AllPaths < Values > > > [ ] ;
2728 getFieldsError : ( ...name : AllPaths < Values > [ ] ) => Record < AllPaths < Values > , string [ ] > ;
2829 getFieldsWarning : ( ...name : AllPaths < Values > [ ] ) => Record < AllPaths < Values > , string [ ] > ;
2930 getFieldWarning : ( name : AllPaths < Values > ) => string [ ] ;
@@ -37,14 +38,28 @@ export interface StateOptions<Values = any> {
3738 cb : ( value : PathToDeepType < Values , T > , key : T , all : Values , fired : ChangeMask ) => void ,
3839 opt ?: { includeChildren ?: boolean ; mask ?: ChangeMask }
3940 ) => ( ) => void ;
41+ subscribeFields : (
42+ names : AllPaths < Values > [ ] ,
43+ cb : (
44+ value : PathToDeepType < Values , AllPaths < Values > > ,
45+ key : AllPaths < Values > ,
46+ all : Values ,
47+ fired : ChangeMask
48+ ) => void ,
49+ opt ?: { includeChildren ?: boolean ; mask ?: ChangeMask }
50+ ) => ( ) => void ;
51+ }
52+
53+ export interface ValidateFieldsOptions extends ValidateOptions {
54+ dirty ?: boolean ;
4055}
4156
4257export interface OperationOptions < Values = any > {
4358 resetFields : ( ...names : AllPaths < Values > [ ] ) => void ;
4459 submit : ( ) => void ;
4560 use : ( mw : Middleware ) => void ;
4661 validateField : ( name : AllPaths < Values > ) => Promise < boolean > ;
47- validateFields : ( ... names : AllPaths < Values > [ ] ) => Promise < boolean > ;
62+ validateFields : ( names ? : AllPaths < Values > [ ] , opts ?: ValidateFieldsOptions ) => Promise < boolean > ;
4863}
4964
5065export interface ValidateErrorEntity < Values = any > {
@@ -157,27 +172,78 @@ export const useFieldState = <Values = any>(
157172 return state ;
158173} ;
159174
175+ export const useFieldsState = < Values = any > (
176+ form : FormInstance < Values > ,
177+ names : AllPaths < Values > [ ] ,
178+ mask : SubscribeMaskOptions = {
179+ errors : true ,
180+ touched : true ,
181+ validated : true ,
182+ validating : true ,
183+ warnings : true
184+ }
185+ ) => {
186+ const state = form . getFields ( names ) ;
187+
188+ // eslint-disable-next-line react/hook-use-state
189+ const [ _ , forceUpdate ] = useState ( { } ) ;
190+
191+ useEffect ( ( ) => {
192+ let unregister : ( ) => void ;
193+ if ( ! names || names . length === 0 ) {
194+ // 监听所有字段
195+ unregister = form . subscribeField (
196+ '' as AllPaths < Values > ,
197+ ( ) => {
198+ flushSync ( ( ) => {
199+ forceUpdate ( { } ) ;
200+ } ) ;
201+ } ,
202+ {
203+ includeChildren : true ,
204+ mask : toMask ( mask )
205+ }
206+ ) ;
207+ } else {
208+ unregister = form . subscribeFields (
209+ names ,
210+ ( ) => {
211+ flushSync ( ( ) => {
212+ forceUpdate ( { } ) ;
213+ } ) ;
214+ } ,
215+ {
216+ mask : toMask ( mask )
217+ }
218+ ) ;
219+ }
220+
221+ // 只监听传入的字段
222+ return unregister ;
223+ } , [ ] ) ;
224+
225+ return state ;
226+ } ;
227+
160228export const useFieldError = < Values = any > ( name : AllPaths < Values > ) => {
161229 const state = useFieldState < Values > ( name , { errors : true } ) ;
162230
163231 return state . errors ;
164232} ;
165233
166- export const useFieldErrors = < Values = any > ( form : FormInstance < Values > ) : Record < AllPaths < Values > , string [ ] > => {
167- const [ errors , setErrors ] = useState ( { } ) ;
168-
169- useEffect ( ( ) => {
170- return form . subscribeField (
171- '' as AllPaths < Values > ,
172- ( ) => {
173- setErrors ( form . getFieldsError ( ) ) ;
174- } ,
175- {
176- includeChildren : true ,
177- mask : ChangeTag . Errors
178- }
179- ) ;
180- } , [ form ] ) ;
234+ export const useFieldErrors = < Values = any > (
235+ form : FormInstance < Values > ,
236+ names : AllPaths < Values > [ ] = [ ]
237+ ) : Record < AllPaths < Values > , string [ ] > => {
238+ const state = useFieldsState < Values > ( form , names , { errors : true } ) ;
239+
240+ const errors = state . reduce (
241+ ( acc , field ) => {
242+ acc [ field . name ] = field . errors ;
243+ return acc ;
244+ } ,
245+ { } as Record < AllPaths < Values > , string [ ] >
246+ ) ;
181247
182248 return errors as Record < AllPaths < Values > , string [ ] > ;
183249} ;
0 commit comments