Интерфейс Material и форма react-hooks с проверкой yup с использованием typescript

#typescript #material-ui #react-hook-form

#typescript #материал-пользовательский интерфейс #форма с реагирующими крючками

Вопрос:

Я пытаюсь использовать React с Material UI и react-hook-form и yup для проверки. Я совершенно новичок во всех частях этого списка. Я хотел бы настроить очень простую форму входа в систему, используя хорошо известную страницу входа в Material UI и Typescript.

На данный момент у меня очень простая проверка, применяется только «обязательное» правило. Я ожидаю, что когда я нажму «Отправить», не заполняя форму, я бы хотел увидеть сообщение об ошибке под вводом, которое должно стать красным. Точно так же, как в примерах, показанных на официальной странице пользовательского интерфейса Material. И здесь возникает проблема… Когда я нажимаю «Отправить», не заполняя форму, фокус переходит на первое поле с ошибкой, и на странице появляется всплывающая подсказка, но не под компонентом, она больше похожа на «плавающий пузырь», а контур не становится красным. (Даже сообщение об ошибке в плавающем пузырьке является чем-то общим, а не тем, что я установил в yup для отображения.)

Я также попытался оставить yup вне проекта и просто использовать атрибут rules, но это привело к тому же. вещи.

Итак, вот компоненты, которые я использую. Извините, если это займет слишком много времени, я еще не выделил место для хранения для этого проекта:

 import Button from '@material-ui/core/Button';
import Checkbox from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import { FormTextField } from '@tao-ui-shared/form';
import React from 'react';
import { FormProvider } from 'react-hook-form';

import { getLogger } from '../../logging/authenticationLogging';
import { useLoginForm } from '../hooks/useLoginForm';
import { useStyles } from './login.styles';

type LoginFormParams = {
    name?: string;
    classes?: Record<'submit' | 'form', string>
}

export const LoginForm: React.FC<LoginFormParams> = ({
    name
}) => {
    // eslint-disable-next-line @typescript-eslint/unbound-method
    const { register, onSubmit, errors, ...methods } = useLoginForm();

    return (
        <FormProvider register={register} errors={errors} {...methods} >
            <form onSubmit={onSubmit}>
                <FormTextField
                    name='email'
                    id='email'
                    variant='outlined'
                    autoComplete='email'
                    label='Email'
                    required
                    autoFocus
                    errors={errors}
                />
                <FormTextField
                    name='password'
                    id='password'
                    variant='outlined'
                    required
                    fullWidth
                    label='Password'
                    type='password'
                    autoComplete='current-password'
                    errors={errors}
                />
                <Button
                    type="submit"
                    fullWidth variant="contained"
                    color="primary"
                >
                        Login
                </Button>
            </form>
        </FormProvider>
    );
}
  
 import { FormControl, TextField } from '@material-ui/core';
import React, { useEffect, useRef } from 'react';
import { Controller } from 'react-hook-form';


export type FormTextFieldParams = {
    readonly name: string;
    readonly id: string;
    readonly variant: 'standard' | 'filled' | 'outlined' | undefined;
    readonly defaultValue?: string;
    readonly label?: string;
    readonly margin?:  'normal' | 'none' | 'dense' | undefined;
    readonly autoComplete?: string;
    readonly type?: string;
    readonly disabled?: boolean;
    readonly required?: boolean;
    readonly fullWidth?: boolean;
    readonly autoFocus?: boolean;
    readonly minLength?: number;
    readonly maxLength?: number;
    readonly errors?: any;
};

export const FormTextField: React.FC<FormTextFieldParams> = ({
    id,
    name,
    errors,
    variant = 'standard',
    margin = 'normal',
    label = '',
    type = '',
    disabled = false,
    required = false,
    fullWidth = true,
    autoFocus = false,
    autoComplete= ''

}) => {
    const isError = useRef(false);
    const errorMessage = useRef('');

    useEffect(() => {
        Object.prototype.hasOwnProperty.call(errors, name);
        if (errors amp;amp; Object.prototype.hasOwnProperty.call(errors, name)) {
            isError.current = true;
            // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
            errorMessage.current = errors[name].message;
        }
    }, [errors, name]);


    return (
        <FormControl fullWidth={fullWidth}>
            <Controller
                as={TextField}
                id={id}
                name={name}
                variant={variant}
                margin={margin}
                label={label}
                type={type}
                required={required}
                fullWidth={fullWidth}
                autoFocus={autoFocus}
                autoComplete={autoComplete}
                error={isError.current}
                helperText={errorMessage.current}
                disabled={disabled}
            />
        </FormControl>
    );
};
  
 import { yupResolver } from '@hookform/resolvers';
import { login } from '@tao-ui-shared/authentication-service'
import { useCallback, useMemo } from 'react';
import { useForm } from 'react-hook-form';
import { useDispatch } from 'react-redux';
import * as yup from 'yup';

import { LoginFormData } from '../../types';


export const useLoginForm = () => {
    const validationSchema = useMemo(() => (
        yup.object().shape({
            email: yup.string().required('Username/email is required'),
            password: yup.string().required('Password is required')
        })
    ), []);

    const methods = useForm<LoginFormData>({
        resolver: yupResolver(validationSchema)
    });

    const dispatch = useDispatch();

    const onSubmit = useCallback((formValues: LoginFormData) => {
       // Do some logging here and dispatch and action...
    }, []);

    return {
        ...methods,
        onSubmit: methods.handleSubmit(onSubmit)
    }
}
  

Любые идеи, что я делаю не так, были бы очень признательны!

Обновить:

Я немного поиграл с этим.И, вероятно, каким-то образом мне нужно, чтобы мой компонент поля формы обертывания был повторно отображен, потому что этого не происходит, когда проверка завершается неудачей… Может ли это быть проблемой?

Комментарии:

1. Я не знаком с формой react-hook, но я уверен, что вам нужно будет передать конкретное поле errors объекта в поля ввода. Поскольку вы «новичок в этом», вы можете захотеть взглянуть на rff-mui , поскольку он глубоко и хорошо интегрируется с material-ui и yup

2. @japrescott: Спасибо за предложение. Да, я посмотрел на final form, потому что это то, что мы используем на моем рабочем месте. Я только что перешел из бэкэнда в full stack, и я все еще не знаю этот мир React настолько глубоко, как я ожидал от себя. Но я это понимаю, и я также видел и читал некоторые узкие места окончательной формы. Особенно, когда у вас много полей формы на экране, например, с многостраничными формами. В этом приложении, над которым я сейчас работаю, если мне удастся пройти страницу входа в систему :-), это будет сценарий. Множество полей в многостраничной форме.

3. finalform позволяет вам очень хорошо контролировать, когда что следует повторно использовать с subscription помощью prop.