All files FormItem.tsx

95.65% Statements 22/23
85.71% Branches 6/7
71.42% Functions 5/7
100% Lines 19/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 672x 2x   2x                   2x                             2x 6x 6x 4x 4x   6x             14x 14x 14x 14x 14x                   2x 18x 10x             8x    
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<BaseFormItemProps, 'validateStatus' | 'validateMessage'> {
  state?: IState<unknown>
}
 
// Input 向上(`FormItem`)注册 state 的函数
type StateRegisterFn = (state?: IState<unknown>) => void
 
// Input 向上(`FormItem`)注册 state 的 context
const stateRegisterCtx = createContext<StateRegisterFn>(() => 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 (
    <stateRegisterCtx.Provider value={noop}>
      {children}
    </stateRegisterCtx.Provider>
  )
}
 
export default observer(function FormItem({ state: stateFromProps, children, ...restProps }: Props) {
  const [innerState, setInnerState] = useState<IState<unknown>>()
  const state = stateFromProps ?? innerState
  const validateProps = state != null ? bindFormItem(state) : null
  return (
    <stateRegisterCtx.Provider value={setInnerState}>
      <BaseFormItem {...validateProps} {...restProps}>
        {children}
      </BaseFormItem>
    </stateRegisterCtx.Provider>
  )
})
 
/** 将 formstate-x state 绑定到 icecream FormItem 的辅助函数 */
export function bindFormItem(state: IState): Pick<BaseFormItemProps, 'validateStatus' | 'validateMessage'> {
  if (state.hasOwnError) {
    return {
      validateStatus: 'error',
      validateMessage: state.ownError
    }
  }
  // TODO: 有些 form item 需要在校验成功时显示成功样式(绿边)
  // 即 `validateStatus: 'success'`,需要支持下
  return { validateStatus: 'default' }
}