Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Problema no envio de um formulario com React

Problema no envio de formulário personalizado

Oi pessoal,

Estou enfrentando um problema ao criar um formulário de cadastro de alunos. Quando tento enviar o formulário, a função de envio não está sendo chamada. Fiz um teste com um formulário simples, usando apenas as tags nativas de HTML, e ele funcionou normalmente. No entanto, quando utilizo meu formulário personalizado, que usa componentes próprios de Input e Button que criei, o envio não funciona de jeito nenhum.

Alguém pode me ajudar a identificar o que pode estar acontecendo?

Abaixo está o código do formulário para análise. Agradeço desde já pela ajuda!

6 respostas
export const CadastroAluno = () => {

    const navigate = useNavigate();

    const {
        register,
        handleSubmit,
        formState: { errors },
    } = useForm<SechemaType>({
        resolver: zodResolver(schemaForm),
    });

    type SechemaType = z.infer<typeof schemaForm>;

    const handleSubmitForm: SubmitHandler<SechemaType> = async (data) => {
        try {
            const response = await axios.post('http://localhost:3001/aluno', data);
            if (response.status === 201 || response.status === 200) {
                alert("Aluno cadastrado com sucesso!");
                navigate("/");
            }
        } catch (error) {
            console.error("Erro ao cadastrar o aluno:", error);
            alert("Ocorreu um erro ao cadastrar o aluno. Tente novamente.");  // Mensagem de erro
        }
    };

    const onSubmit = handleSubmit(handleSubmitForm);

    return (
        <div className="flex flex-col items-center ">
            <DiagonalSection text="Cadastro" subtext="Aluno" />
            <div className="absolute w-[90%] mt-28 lg:ml-2 lg:mt-[245px] lg:mr-20 flex flex-col">
                <form
                    className="flex flex-col gap-6 mb-6"
                    onSubmit={onSubmit}
                >


                    <div className="flex flex-col lg:flex-row gap-4">
                        <div className="lg:w-full">
                            <Input
                                type={InputType.Text}
                                label="Nome do Aluno"
                                error={errors.name?.message}
                                register={{ ...register("name") }}
                            />
                        </div>

                        <div className="lg:w-full">
                            <Input
                                type={InputType.CPF}
                                label="CPF"
                                placeholder="000.000.000-00"
                                error={errors.cpf?.message}
                                register={{ ...register("cpf") }}
                            />
                        </div>
                        
                        {... Outros campos}
                        
                        <div className="flex items-center lg:justify-end justify-center mt-4">
                        <div>
                            <Button children="Cancelar" variant="transparent" onClick={() => navigate('/')} />
                        </div>

                        <div>
                            <Button children="Cadastrar" variant="solid" type="submit" />
                        </div>
                    </div>
                      </form>
            </div>
        </div>
    );
type ButtonProps = {
    variant: 'solid' | 'transparent';
    children: ReactNode;
    type?: 'submit' | 'button';
    size?: 'small' | 'medium' | 'large';
    onClick?: () => void;
};

export const Button = ({ children, variant, type = 'submit', size = 'medium', onClick }: ButtonProps) => {
    const baseStyles = 'rounded-xl font-semibold';
    const solidStyles = 'bg-blue-600 text-white hover:bg-blue-700';
    const outlineStyles = 'bg-transparent text-blue-600';

    const sizeStyles = {
        small: 'h-6 w-20 text-sm',
        medium: 'h-8 w-28 text-base',
        large: 'h-9 w-32 text-lg',
    };

    return (
        <button
            className={twMerge(
                baseStyles,
                sizeStyles[size], // Aplica o estilo com base no tamanho
                variant === 'solid' ? solidStyles : outlineStyles
            )}
            type={type}
            onClick={onClick}
        >
            {children}
        </button>
    );
};
type InputProps = {
    type: InputType,
    placeholder?: string
    label: string,
    error?: string,
    register?: UseFormRegisterReturn,
    onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
} & InputHTMLAttributes<HTMLInputElement>;

export const Input = ({ type, placeholder, label, error, register, onChange }: InputProps) => {
    const inputRef = useRef(null);

    const getMask = (type: string) => {
        switch (type) {
            case InputType.CNPJ:
                return "99.999.999/9999-99";
            case InputType.CPF:
                return "999.999.999-99";
            case InputType.CEP:
                return "99999-999";
            case InputType.Phone:
                return "(99) 99999-9999";
            default:
                return null;
        }
    };

    const mask = getMask(type);

    const ufOptions = [
        "AC", "AL", "AM", "AP", "BA", "CE", "DF", "ES", "GO", "MA",
        "MT", "MS", "MG", "PA", "PB", "PR", "PE", "PI", "RJ", "RN",
        "RS", "RO", "RR", "SC", "SP", "SE", "TO"
    ];

    return (
        <div className="w-full flex flex-col gap-2">
            <label className={'px-2 text-lg font-bold'}>
                {label}
            </label>
            {type === "UF" ? (
                <select
                    className={twMerge(
                        'border-2 border-colorMenuSecondary rounded-full px-4 py-2 w-full focus:outline-none focus:border-2 focus:border-colorMenuPrimary appearance-none relative',
                        'custom-select'
                    )}
                    {...register}

                >
                    <option value="">Selecione o Estado</option>
                    {ufOptions.map(uf => (
                        <option key={uf} value={uf}>
                            {uf}
                        </option>
                    ))}
                </select>
            ) : mask ? (
                <InputMask
                    ref={inputRef}
                    mask={mask}
                    placeholder={placeholder}
                    type={type}
                    className={twMerge(
                        'border-2 border-colorMenuSecondary rounded-full px-4 py-2 w-full focus:outline-none focus:border-2 focus:border-colorMenuPrimary',
                    )}
                    {...register}
                    onChange={onChange}
                />
            ) :
                (
                    <input
                        type={type}
                        ref={inputRef}
                        placeholder={placeholder}
                        className={twMerge(
                            'border-2 border-colorMenuSecondary rounded-full px-4 py-2 w-full focus:outline-none focus:border-2 focus:border-colorMenuPrimary',
                        )}
                        {...register}
                        onChange={onChange}
                    />
                )}
            {error && (
                <p className="text-red-500 text-xs ml-4">{error}</p>
            )}
        </div>
    );
};
import { z } from 'zod';

const validateCPF = (cpf: string) => {
    return /^\d{11}$/.test(cpf);
};

const validateCNPJ = (cnpj: string) => {
    return /^\d{14}$/.test(cnpj);
};

export const schemaForm = z.object({
    name: z.string().min(2, { message: "* Nome é obrigatório" }),

    registration: z.string().min(1, { message: "* Preencher o campo da matrícula é obrigatório" }),

    email: z.string()
        .min(1, { message: "* Email é obrigatório" })
        .email({ message: "Email inválido" }),

    confirmEmail: z.string()
        .min(1, { message: "* Confirmação de email é obrigatória" })
        .email({ message: "Confirmação de email inválida" }),

    cnpj: z.string()
        .min(1, { message: "* CNPJ é obrigatório" })
        .refine(value => validateCNPJ(value.replace(/[^\d]+/g, '')), { message: "CNPJ inválido" }),

    cpf: z.string()
        .min(1, { message: "* CPF é obrigatório" })
        .refine(value => validateCPF(value.replace(/[^\d]/g, '')), { message: "CPF inválido" }),


    phone: z.string()
        .refine(value => {
            const cleanedPhone = value.replace(/[^\d]+/g, '');
            return cleanedPhone.length >= 10 && cleanedPhone.length <= 11;
        }, { message: "Telefone inválido" })
        .transform(value => value.replace(/[^\d]+/g, '')),

    uf: z.string()
        .min(2, { message: "* UF é obrigatória" })
        .max(2, { message: "UF deve conter exatamente 2 caracteres" }),

    cep: z.string()
        .min(8, { message: "* CEP é obriatório" })
        .max(10, { message: "CEP deve conter no máximo 10 dígitos" })
        .refine(value => value.replace(/[^\d]/g, ''), { message: "CEP inválido" }),

    city: z.string().min(1, { message: "* Cidade é obrigatória" }),

    neighborhood: z.string().min(1, { message: "* Bairro é obrigatório" }),

    road: z.string().min(1, { message: "* Rua é obrigatória" }),

    password: z.string()
        .min(8, { message: "A senha deve conter pelo menos 8 caracteres" }),

    confirmPassword: z.string()
        .min(6, { message: "* A confirmação de senha é obrigatória" }),

    periodo: z.string().min(1, { message: "* Informar o período é obrigatório" }),

    curso: z.string().min(1, { message: "* Nome do Curso é obrigatório" }),

    matricula: z.string().min(1, { message: "* Matrícula é obrigatória" }),


})
    .superRefine((data, ctx) => {
        if (data.email !== data.confirmEmail) {
            ctx.addIssue({
                code: "custom",
                message: "Emails não correspondem",
                path: ["confirmEmail"], // Caminho onde o erro será adicionado
            });
        }

        if (data.password !== data.confirmPassword) {
            ctx.addIssue({
                code: "custom",
                message: "As senhas não correspondem",
                path: ["confirmPassword"], // Caminho onde o erro será adicionado
            });
        }
    });

Olá, Bruno. Tudo bem?

Ao que parece, a sua função para submeter o formulário não está sendo disparada. Você poderia me dar mais detalhes? Há algum erro ou warning no seu console? Tente verificar se algum outro comportamento está prevenindo o envio do formulário, como um event.preventDefault() não intencional em outro ponto do código.

Se possível, me envie o repositório com o projeto, consigo analisar melhor vendo o código por completo.

Fico no aguardo e à disposição
solução!

"Identifiquei a origem do erro: o problema estava relacionado à validação do CNPJ, que não estava sendo utilizado no formulário. Essa situação impedia o envio do formulário. Para resolver, implementei uma validação com Zod específica para cada tipo de formulário, o que solucionou a questão."