Привет! Это моя первая статья на Хабре и в ней я хочу рассказать о, первой мной написанной, библиотеке для работы с формами. Но сперва, давайте поговорим о том, как должна выглядеть идеальная библиотека для работы с формами.

  1. Синтаксис будто создан для написания форм.

  2. Формы просто дебажить и, еще проще, облагать их тестами.

  3. Полная инкапсуляция логики конкретных инпутов.

  4. Компоненты инпут��в должны быть реюзабельными.

  5. Конечная форма должна быть максимально абстрактной и легко читаемой.

А теперь, давайте посмотрим, как выглядит форма написанная с помощью react-redux-hook-form.

export const Login:FC = memo( () => {
  const form = useForm({name: 'login'})

  return (
    <WrapperForm form={form} onSubmit={(data:ILogin) => {User.options.login(data)}}>
      <InputTitleStyled>Номер телефона</InputTitleStyled>
      <PhoneInput name={'username'} required/>

      <InputTitleStyled>Пароль</InputTitleStyled>
      <PasswordInput name={'password'} required/>

      <SubmitButton text={'Авторизироваться'}/>
    </WrapperForm>
  )
})

Здесь мы используем хук useForm и обертку для формы WrapperForm.

useForm - хук для инициализации формы, принимает name и возвращает объект с методами для работы с формой. form.reset, form.changeDataField, form.changeIsValidateField, form.changeMessageErrorField, form.useFieldSelector. Полагаю, не имеет смысла описывать принцип работы каждого метода.

WrapperForm - обертка формы в которую обязательно должна быть обернута форма. Принимает параметры: onChange, initialValue, onSubmit.

Теперь давайте посмотрим, как создаются инпуты.

interface IEmail {
  name: string;
  required?: boolean;
}

export const EmailInput: FC<IEmail> = (props) => {
  const {useData, useIsValidate, useIsTouch, useMessageError} = useField({
    name: props.name,
    isRequired: props.required,
    validateFunction: (value:string) => validate(value).string().max(30).email(),
  })

  const [data, changeData] = useData()
  const [isValidate, ] = useIsValidate()
  const [isTouch, ] = useIsTouch()
  const [messageError, ] = useMessageError()

  return (
    <>
      <EmailInputStyled
        type={"email"} 
				value={data}
        onChange={(e:any)=> {changeData(e.target.value)}}
        isValidate={isValidate || !isTouch}
        id={props.name}
      />
      {!isValidate && isTouch &&
        <ErrorTitleStyled className={'error'}>
          {messageError}
        </ErrorTitleStyled>
      }
    </>
  )
};

Здесь мы используем useField. useField - инициализирует поле и из него достаются хуки для работы с полем. useData, useIsValidate, useIsTouch, useMessageError, работают по принципу useState. Validate/IsTouch/MessageError - автоматически обрабатываются, но если вы хотите самостоятельно обрабатывать данные поля, библиотека позволит вам это сделать. Для того, чтобы отменить автоматическую обработку, необходимо в useField передать isDisableAuto: true.

Параметры useField: name, initialValue, isRequired, isDisableAuto, messageError, isTouch, isValidate, validateFunction.

name: название поля. Все поля должны быть уникальными.

initialValue: стандартное значение. Стоит использовать для того, чтобы явно указать, в каком формате будут храниться данные в данном инпуте. Перебивается initialValues с формы.

Теперь давайте посмотрим, как создать кнопку, для данной формы.

interface ISubmitButton {
  text: string;
  className?: string;
}

export const SubmitButton: FC<ISubmitButton> = (props) => {
  const formName = useContext(formNameContext)
  const isValidForm = useIsValidForm(formName)
  const onSubmit = useContext(onSubmitContext)

  const onClick = () => {
    if(isValidForm){
      onSubmitForm(formName, onSubmit)
    }
  }
  return (
    <SubmitButtonStyled className={props.className} isValidForm={isValidForm} onClick={onClick}>
      {props.text}
    </SubmitButtonStyled>
  )
};

В данном примере мы достаем из контекста formName и onSubmit.

Также мы используем useIsValidateForm хук, в которым передаем имя формы.

Так-как мы используем redux. То мы можем использовать данные формы в других местах.

Например:

const search = useFieldSelector({formName: 'searchForm', fieldName: 'search'})

Подключение react-redux-hook-form.

Сперва установим: npm i react-redux-hook-form.

Затем надо модифицировать стор.

1) Подключим formControllerReducer.

import { formControllerReducer } from 'react-redux-hook-form';

const combinedReducer = combineReducers({
  formController: formControllerReducer, // подключение formControllerReducer
  user: userReducer,
  contact: contactReducer,
});

2) Запишем функцию getState в window.

window.getState = () => store.getState()

В общем-то это все что нужно знать для того чтобы использовать react-redux-hook-form.

Так как мы используем redux, мы можем дебажить форму через redux-devtools(расширения гугл хрома).

Думаю, еще стоит уделить пару строчек себе. У меня не так много коммерческого опыта, всего лишь 1.5 года. да и то треть из этого опыта это работа на бэке. Так что жду предложению по тому, как можно улучшить данную библиотеку :)