import React, { createContext, ReactNode, useContext, useEffect, useState } from 'react' import { observer } from 'mobx-react' import { IState } from 'formstate-x' import { FormItem as BaseFormItem, FormItemProps as BaseFormItemProps } from 'react-icecream' export interface Props extends Omit { state?: IState } // Input 向上(`FormItem`)注册 state 的函数 type StateRegisterFn = (state?: IState) => void // Input 向上(`FormItem`)注册 state 的 context const stateRegisterCtx = createContext(() => undefined) export interface InputWrapperProps { state: IState | undefined children: ReactNode } // eslint-disable-next-line @typescript-eslint/no-empty-function function noop() {} /** * 用来包裹 `FormItem` 中的 Input 组件内容:使得 `FormItem` 可以正确地感知到 Input 组件(及其对应的 state) * 注意适合 `FormItem` 对应单个 Input 组件的场景;若单个 `FormItem` 中包含了多个 Input 组件, * 需要通过给 `FormItem` 显式传递 prop state 来指定 `FormItem` 消费的 state */ export function InputWrapper({ state, children }: InputWrapperProps) { const registerState = useContext(stateRegisterCtx) useEffect(() => { registerState(state) return () => registerState(undefined) }, [registerState, state]) return ( {children} ) } 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: IState): Pick { if (state.hasOwnError) { return { validateStatus: 'error', validateMessage: state.ownError } } // TODO: 有些 form item 需要在校验成功时显示成功样式(绿边) // 即 `validateStatus: 'success'`,需要支持下 return { validateStatus: 'default' } }