diff --git a/packages/core/src/models/Field.ts b/packages/core/src/models/Field.ts index e646c4c2d2f..c375dd4fc4e 100644 --- a/packages/core/src/models/Field.ts +++ b/packages/core/src/models/Field.ts @@ -60,6 +60,8 @@ import { } from '../shared' import { Query } from './Query' +const RESPONSE_REQUEST_DURATION = 100 + export class Field< Decorator extends JSXComponent = any, Component extends JSXComponent = any, @@ -226,8 +228,11 @@ export class Field< this.disposers.push( reaction( () => this.value, - () => { + (value) => { this.form.notify(LifeCycleTypes.ON_FIELD_VALUE_CHANGE, this) + if (isValid(value) && this.modified && !this.caches.inputing) { + this.validate() + } } ), reaction( @@ -581,28 +586,28 @@ export class Field< } setLoading = (loading?: boolean) => { - clearTimeout(this.requests.loader) + clearTimeout(this.requests.loading) if (loading) { - this.requests.loader = setTimeout(() => { + this.requests.loading = setTimeout(() => { batch(() => { this.loading = loading this.form.notify(LifeCycleTypes.ON_FIELD_LOADING, this) }) - }, 100) + }, RESPONSE_REQUEST_DURATION) } else if (this.loading !== loading) { this.loading = loading } } setValidating = (validating?: boolean) => { - clearTimeout(this.requests.validate) + clearTimeout(this.requests.validating) if (validating) { - this.requests.validate = setTimeout(() => { + this.requests.validating = setTimeout(() => { batch(() => { this.validating = validating this.form.notify(LifeCycleTypes.ON_FIELD_VALIDATING, this) }) - }, 100) + }, RESPONSE_REQUEST_DURATION) } else if (this.validating !== validating) { this.validating = validating } @@ -685,6 +690,7 @@ export class Field< } const values = getValuesFromEvent(args) const value = values[0] + this.caches.inputing = true this.inputValue = value this.inputValues = values this.value = value @@ -693,6 +699,7 @@ export class Field< this.form.notify(LifeCycleTypes.ON_FIELD_INPUT_VALUE_CHANGE, this) this.form.notify(LifeCycleTypes.ON_FORM_INPUT_CHANGE, this.form) await this.validate('onInput') + this.caches.inputing = false } onFocus = async (...args: any[]) => { @@ -726,25 +733,41 @@ export class Field< } this.form.notify(LifeCycleTypes.ON_FIELD_VALIDATE_END, this) } - start() - if (!triggerType) { - const allTriggerTypes = parseValidatorDescriptions(this.validator).map( - (desc) => desc.triggerType - ) - const results = {} - for (let i = 0; i < allTriggerTypes.length; i++) { - const payload = await validateToFeedbacks(this, allTriggerTypes[i]) - each(payload, (result, key) => { - results[key] = results[key] || [] - results[key] = results[key].concat(result) - }) + const runner = async () => { + if (!triggerType) { + const allTriggerTypes = parseValidatorDescriptions(this.validator).map( + (desc) => desc.triggerType + ) + const results = {} + for (let i = 0; i < allTriggerTypes.length; i++) { + const payload = await validateToFeedbacks(this, allTriggerTypes[i]) + each(payload, (result, key) => { + results[key] = results[key] || [] + results[key] = results[key].concat(result) + }) + } + end() + return results } + const results = await validateToFeedbacks(this, triggerType) end() + return results } - const results = await validateToFeedbacks(this, triggerType) - end() - return results + + start() + + return new Promise((resolve) => { + cancelAnimationFrame(this.requests.validate) + + this.requests.validate = requestAnimationFrame(() => { + const results = runner() + this.requests.validateResolvers.forEach((resolve) => resolve(results)) + }) + + this.requests.validateResolvers = this.requests.validateResolvers || [] + this.requests.validateResolvers.push(resolve) + }) } reset = async (options?: IFieldResetOptions) => { diff --git a/packages/core/src/models/Form.ts b/packages/core/src/models/Form.ts index d09690f6e86..7a3fb6b99cc 100644 --- a/packages/core/src/models/Form.ts +++ b/packages/core/src/models/Form.ts @@ -59,6 +59,8 @@ import { Graph } from './Graph' const DEV_TOOLS_HOOK = '__FORMILY_DEV_TOOLS_HOOK__' +const RESPONSE_REQUEST_DURATION = 100 + export class Form { displayName = 'Form' id: string @@ -462,7 +464,7 @@ export class Form { this.submitting = submitting this.notify(LifeCycleTypes.ON_FORM_SUBMITTING) }) - }, 100) + }, RESPONSE_REQUEST_DURATION) this.notify(LifeCycleTypes.ON_FORM_SUBMIT_START) } else { if (this.submitting !== submitting) { @@ -480,7 +482,7 @@ export class Form { this.validating = validating this.notify(LifeCycleTypes.ON_FORM_VALIDATING) }) - }, 100) + }, RESPONSE_REQUEST_DURATION) this.notify(LifeCycleTypes.ON_FORM_VALIDATE_START) } else { if (this.validating !== validating) { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 5cbac5e04cd..f39c6a07f72 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -257,8 +257,10 @@ export interface IVoidFieldFactoryProps< } export interface IFieldRequests { - validate?: NodeJS.Timeout - loader?: NodeJS.Timeout + validate?: number + validateResolvers?: Array<(value: any) => void> + validating?: NodeJS.Timeout + loading?: NodeJS.Timeout batch?: () => void } @@ -266,6 +268,7 @@ export interface IFieldCaches { value?: any initialValue?: any feedbacks?: IFieldFeedback[] + inputing?: boolean } export type FieldDisplayTypes = 'none' | 'hidden' | 'visible' | ({} & string)