Executando verificação de segurança...
2

React: Custom Input Profissional

Se você já trabalhou com inputs no React provavelmente conhece a forma padrão de desenvolvimento, mas você tem um minutinho pra ouvir a palavra do forwardRef?

Começando pelo básico

Um componente de input normalmente tem a seguinte estrutura:

function CustomInput() {
    return (
        <input />
    )
}

export { CustomInput }

E as props? value? onChange?

Mas pensando em typescript, como podemos passar todos as props que o input tem para o nosso CustomInput?

A resposta é o criar um interface e que extende (extends) essas propriedade do input, em conjunto com o spread operator (...):

import { InputHTMLAttributes } from "react";

interface CustomInputProps extends InputHTMLAttributes<HTMLInputElement> {
    label: string; // ex.: propriedades adicionais
}

function CustomInput({ label, ...rest }: CustomInputProps) {
    return (
        <input {...rest}/>
    )
};

export { CustomInput };

Só isso é o suficiente?

Não. Em alguns casos de uso sim, mas se você quer que seu design system cubra mais casos de uso, precisa pensar como ela vai se comportar com libs de form, por exemplo: react-hook-form.

Pensando no trabalho que fizemos até então e tentando usar esse componente com o react-hook-form. Ele não vai funcionar.

Isso acontece pois ele não possui ref. O post ficaria longo se eu fosse dar uma resposta completa sobre ref, mas nesse momento pense como uma referência do componente original <input> passada através do componente <CustomInput />.

ForwardRef

Primeiro vamos transformar nossa function em arrow function:

...
const CustomInput = ({ label, ...rest }: CustomInputProps) => {
    ....
}
...

E então importar e usar o forwardRef do React, o forwarRef é uma função que recebe uma arrow function () => {...}, sendo que o primeiro parametro vai ser as props, e o segundo o ref (props, ref) => {...}, sendo assim: forwarRef((props, ref) => {...})

...
import { forwardRef } from "react";
...
const CustomInput = forwardRef<HTMLInputElement, CustomInputProps>((props, ref) => {
    ....
})
...

Se quiser pode usar o spred nas props novamente:

...
const CustomInput = forwardRef<HTMLInputElement, CustomInputProps>(
    ({ label, ...rest }, ref) => {
        ...
    }
)
...

E por fim vamos passar o ref ao input

...
const CustomInput = forwardRef<HTMLInputElement, CustomInputProps>(
    ({ label, ...rest }, ref) => {
        return (
            <input {...rest} ref={ref}/>
        )
    }
)
...

Dessa forma seu componente está pronto para ser usado por libs de form.

Dica extra

Se você percebeu nós não usamos o label até então, bem parece simples aplicar o label, mas nem tanto, por causa do id.

Como o id é preciso passar para o <input /> e para htmlFor no <label />, o que podemos fazer sobre isso?

Opção 1:

Seria tornar uma propriedate obrigatória:

interface CustomInputProps extends InputHTMLAttributes<HTMLInputElement> {
    label: string;
    id: string;
}

Mas lembre-se que queremos imitar o comportamento do input, e ele não tem id obrigatório e é aí que chegamos a segunda opção:

Opção 2:

Usando o novo hook do react, o useId ❤️:

import { useId } from "react";

E nosso componente finalizado ficaria da seguinte forma:

import { useId, InputHTMLAttributes, forwardRef } from "react";

interface CustomInputProps extends InputHTMLAttributes<HTMLInputElement> {
    label: string;
}

const CustomInput = forwardRef<HTMLInputElement, CustomInputProps>(
    ({ label, ...rest }, ref) => {
        const id = useId();
    
        return (
            <>
                <label htmlFor={id}>
                    {label}
                </label>
                
                <input {...rest} ref={ref} id={id} />
            </>
        )
    }
)

export { CustomInput }

Claro, há sempre melhorias que podem ser feitas, como adicionar a className, não permitir o id já que usamos o useId.

Mas a ideia desse post é trazer uma forma mais completa de criação de componente de formulário. E isso serve também para checkbox, select, radio e etc.

Author: Dan Sousa

Carregando publicação patrocinada...
1

Caramba! Estou iniciando no react e é sempre bom ter outros exemplos pra saber se estou seguindo certo nas boas práticas! Não conhecia o react-hook-form, vou dar uma olhada!