import React, { createContext, useContext, useEffect, useState } from 'react' import { observer } from 'mobx-react' import { ComposibleValidatable as State, FormState } from 'formstate-x' import { FormItem as BaseFormItem, FormItemProps as BaseFormItemProps } from '../Form' export interface Props extends Omit { state?: State } // Input 向上(`FormItem`)注册 state 的函数 type StateRegisterFn = (state?: State) => void // Input 向上(`FormItem`)注册 state 的 context const stateRegisterCtx = createContext(() => undefined) /** * `FormItem` 中的 Input 组件通过这个 hook 把 state 传递给 `FormItem` * 注意适合 `FormItem` 对应单个 Input 组件的场景;若单个 `FormItem` 中包含了多个 Input 组件, * 需要通过给 `FormItem` 显式传递 prop state 来指定 `FormItem` 消费的 state */ export function useStateInFormItem(state?: State) { const registerState = useContext(stateRegisterCtx) useEffect(() => { registerState(state) return () => registerState(undefined) }, [registerState, state]) } export default observer(function FormItem({ state: stateFromProps, children, ...restProps }: Props) { const [innerState, setInnerState] = useState>() const state = stateFromProps ?? innerState const validateProps = state != null ? bindFormItem(state) : null return ( {children} ) }) /* 将 formstate-x state 绑定到 icecream FormItem 的辅助函数 */ export function bindFormItem(state: State): Pick { let { hasError, error } = state // 在 form state 对应的界面层级,只需要展示 form 自身的校验结果即可 // field 的校验结果应该展示在 field 对应的界面层级(通过 bindFormItem field 实现) if (state instanceof FormState) { hasError = state.hasOwnError error = state.ownError } if (hasError) { return { validateStatus: 'error', validateMessage: error } } // TODO: 有些 form item 需要在校验成功时显示成功样式(绿边) // 即 `validateStatus: 'success'`,需要支持下 return { validateStatus: 'default' } }