import { useMemo } from 'react'; import { FieldModel, BasicModel, FormStrategy, ModelRef, isModelRef, isFieldModel, } from './models'; import { IFormContext, useFormContext } from './context'; import { IValidators } from './validate'; import { useDestroyOnUnmount } from './utils'; import { or } from './maybe'; import { createModelNotFoundError, createUnexpectedModelTypeError, } from './error'; import { useObservableEagerState } from 'observable-hooks'; function isValueFactory( candidate: Value | (() => Value) ): candidate is () => Value { return typeof candidate === 'function'; } function useModelAndChildProps( ctx: IFormContext | null, field: FieldModel | ModelRef> | string, defaultValue: Value | (() => Value) ) { const model = useMemo(() => { let model: FieldModel; if (typeof field === 'string') { const { strategy, parent } = ctx ?? {}; const m = parent.get(field); if (strategy === FormStrategy.View) { if (!m || !isFieldModel(m)) { const v = or( parent.getPatchedValue(field), isValueFactory(defaultValue) ? defaultValue : () => defaultValue ); model = new FieldModel(v); parent.registerChild(field, model as BasicModel); } else { model = m; } } else { if (!m) { throw createModelNotFoundError(field); } else if (!isFieldModel(m)) { throw createUnexpectedModelTypeError(field, 'FieldModel', m); } 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); field.setModel(model); } else { model = m; } } else { model = field; } return model; }, [field, ctx?.parent, ctx?.strategy, ctx?.form]); // eslint-disable-line react-hooks/exhaustive-deps return model; } /** * 获取一个 `Field`。 * * `Model` 模式下传入字符串类型的 `field` 时, `validators` 和 `defaultValue` 均无效。 * * @param field 字段名 * @param validators 校验函数数组 * @param defaultValue 默认值 */ export function useField( field: string | ModelRef>, defaultValue: Value | (() => Value), validators?: IValidators ): FieldModel; /** * 获取一个 `Field` * * @param field `Field` 对应的 model 对象,用于关联 `Field` 和 model;当 `FormStrategy` 是 `Model` 或渲染 `FieldArray` 的时候才能使用 */ export function useField( field: FieldModel | ModelRef> ): FieldModel; export function useField( field: FieldModel | ModelRef> | string, defaultValue?: Value | (() => Value), validators: IValidators = [] ): FieldModel { const ctx = useFormContext(typeof field !== 'string'); const model = useModelAndChildProps(ctx, field, defaultValue); useObservableEagerState(model.value$); useObservableEagerState(model.error$); // Only update validators in View mode if ( ctx?.strategy === FormStrategy.View && (typeof field === 'string' || isModelRef(field)) ) { model.validators = validators; } useDestroyOnUnmount(field, model, ctx?.parent); return model; }