Escrevendo uma API para componentes React, parte 1: não crie objetos conflitantes
Escrevendo uma API para componentes de reação, parte 2: dê nomes ao comportamento, não à interação
Escrevendo uma API para componentes React, parte 3: a ordem dos adereços é importante
Escrevendo uma API para reagir componentes, parte 4: Cuidado com o Apropacalypse!
Escrevendo uma API para reagir componentes, parte 5: apenas usar composição
Escrevemos API para componentes React, parte 6: criamos comunicação entre componentes
Vamos falar sobre formulários.
Provavelmente você lê vários artigos sobre gerenciamento de state
em formulários, mas esse não é um desses artigos. Em vez disso, quero falar sobre como os formulários e suas APIs funcionam.

Há muita coisa acontecendo aqui, dê uma olhada na API
<Form layout="label-on-left"> <Form.Field label="Name"> <TextInput type="text" placeholder="Enter your name" /> </Form.Field> <Form.Field label="Email"> <TextInput type="email" placeholder="email@domain.com" /> </Form.Field> </Form>
Vamos examinar cada um dos componentes e analisá-los:
Tudo começa com o componente Form
, que é o elemento básico do formulário com uma classe anexada. Ele renderizará tudo o que você colocar nele.
function Form(props) { return <form className="form">{props.children}</form> } render(<Form layout="label-on-left">...</Form>)
Também aceita layout
suporte, o que é útil quando você tem pouco espaço.

<Form layout="label-on-top">...</Form>
Isso altera a maneira como os rótulos são alinhados (da direita para a esquerda) e como a margin
funciona.
O formulário não controla a largura e a margin
seu conteúdo interno. Isso já é uma preocupação para o campo de entrada dentro deste formulário.
Portanto, o componente Form
deve relatar informações de layout
abaixo.
A maneira mais fácil seria passar o layout
usando adereços, mas o conteúdo do formulário é dinâmico (determinado pelo desenvolvedor que o usa), não sabemos exatamente qual será o formulário.
É aqui que a API de contexto nos ajuda.
const LayoutContext = React.createContext() function Form(props) { return ( <form className="form"> <LayoutContext.Provider value={{ layout: props.layout }} > {props.children} </LayoutContext.Provider> </form> ) } export default Form export { LayoutContext }
Agora o campo do formulário pode usar esse contexto e obter o valor do layout
O componente FormField
(campo de entrada do formulário) adiciona um label
a tudo o que você coloca nele (por exemplo, entrada de texto).
function Field(props) { return ( <div className="form-field"> <label {...props}>{props.label}</label> {props.children} </div> ) }
Além disso, ele adiciona uma classe para o layout
- que vem do contexto que criamos no componente Form
.
import { LayoutContext } from './form' function Field(props) { return ( <LayoutContext.Consumer> {context => ( <div className={`form-field ${context.layout}`}> <label {...props}>{props.label}</label> {props.children} </div> )} </LayoutContext.Consumer> ) }
React 16.8+ useContext hook facilita a sintaxe
import { LayoutContext } from './form' function Field(props) { const context = useContext(LayoutContext) return ( <div className={`form-field ${context.layout}`}> <label {...props}>{props.label}</label> {props.children} </div> ) }
Se você estiver interessado, aqui está o código css:
.form-field.label-on-left { max-width: 625px; display: flex; align-items: center; } .form-field.label-on-left label { text-align: right; width: 175px; margin-right: 25px; } .form-field.label-on-top { width: 100%; display: block; } .form-field.label-on-top label { text-align: left; margin-bottom: 25px; }
O último detalhe sobre o qual quero falar é sobre essa estranha sintaxe pontilhada para componentes.
Como o Field
(o campo de entrada) é sempre usado com um formulário, faz sentido agrupá-los.
Uma maneira de fazer isso é exportá-lo do mesmo arquivo:
import Field from './field' function Form(props) { } export default Form export { Field }
E agora os usuários podem importá-los juntos:
import Form, { Field } from 'components/form' render( <Form> <Field>...</Field> </Form> )
Podemos fazer uma pequena melhoria anexando Field
ao próprio componente do formulário.
import Field from './field' function Form(props) { } Form.Field = Field export default Form
Esse código funciona porque os componentes React são objetos javascript e você pode adicionar chaves adicionais a esses objetos.
Para o usuário, isso significa que, quando ele importa um Form
, ele recebe um Field
automaticamente.
import Form from 'components/form' render( <Form> <Form.Field>...</Form.Field> </Form> )
Eu realmente gosto dessa API, que torna óbvia a conexão entre Form
e Form.Field
.
Nota: você deve mover o contexto para outro arquivo para evitar dependência circular.
A combinação de sintaxe com pontos e contexto torna nosso componente Form
inteligente, mantendo sua operabilidade para composições (compostas).