import { useMemo, useEffect } from 'react'; import { FieldModel, BasicModel, FormStrategy, FieldSetModel, FormModel, ModelRef, isModelRef, isFieldModel, } from './models'; import { useValue$ } from './hooks'; import { useFormContext } from './context'; import { IValidators } from './validate'; import { removeOnUnmount } from './utils'; import { or } from './maybe'; import { UnexpectedFormStrategyError } from './error'; function isValueFactory(candidate: Value | (() => Value)): candidate is () => Value { return typeof candidate === 'function'; } function useModelAndChildProps( field: FieldModel | ModelRef> | string, parent: FieldSetModel, strategy: FormStrategy, defaultValue: Value | (() => Value), form: FormModel, ) { const { model, effect } = useMemo(() => { let model: FieldModel; let effect: (() => void) | undefined; if (typeof field === 'string') { if (strategy !== FormStrategy.View) { throw UnexpectedFormStrategyError; } const m = parent.get(field); if (!m || !isFieldModel(m)) { const v = or( parent.getPatchedValue(field), isValueFactory(defaultValue) ? defaultValue : () => defaultValue, ); model = new FieldModel(v); effect = () => parent.registerChild(field, model as BasicModel); } else { model = m; } } else if (isModelRef>(field)) { const m = field.getModel(); if (!m || !isFieldModel(m)) { const v = or(field.patchedValue, () => or(field.initialValue, isValueFactory(defaultValue) ? defaultValue : () => defaultValue), ); model = new FieldModel(v); effect = () => field.setModel(model); } else { model = m; } } else { model = field; } return { model, effect }; }, [field, parent, strategy, form]); useEffect(() => effect?.(), [effect]); return model; } /** * 创建一个 `Field` * * @param field 字段名,当 `FormStrategy` 是 `View` 的时候才能用字段名 * @param validators 当 `field` 是字段名的时候,可以传入`validator` * @param defaultValue 默认值 */ export function useField( field: string | ModelRef>, defaultValue: Value | (() => Value), validators?: IValidators, ): FieldModel; /** * 创建一个 `Field` * * @param field `Field` 对应的 model 对象,用于关联 `Field` 和 model;当 `FormStrategy` 是 `Model` 的时候才能用 */ export function useField(field: FieldModel): FieldModel; export function useField( field: FieldModel | ModelRef> | string, defaultValue?: Value | (() => Value), validators: IValidators = [], ): FieldModel { const { parent, strategy, form } = useFormContext(); const model = useModelAndChildProps(field, parent, strategy, defaultValue!, form); const { value$, error$ } = model; useValue$(value$, value$.getValue()); useValue$(error$, error$.getValue()); if (typeof field === 'string' || isModelRef(field)) { model.validators = validators; } removeOnUnmount(field, model, parent); return model; }