fbpx

Introdução

Raros são os sistemas que não possuem qualquer tipo de formulários. Login, cadastros, edições, pesquisas e wizards são alguns dos exemplos de forms que estão presentes na grande maioria dos aplicativos e sites.

A primeiro momento, gerenciar esses forms parece ser uma tarefa simples: o usuário preenche os campos e, posteriormente, os dados são enviados para algum lugar.

Entretanto as coisas podem ficar mais complexas devido às peculiaridades ou até mesmo o tamanho do sistema. Por isso é necessário encontrar uma forma de padronizar esse processo de criação dos formulários.

Praticamente todos os formulários devem ter validações client-side para melhorar a experiência do usuário, afinal ninguém quer preencher um formulário gigantesco sem saber se está digitando as informações como o esperado ou não.

Além disso, é comum que os mesmos formulários apareçam em diferentes partes da aplicação mas de formas diferentes, por exemplo, ora inserindo dados ora editando dados previamente já inseridos. 

Todavia, implementar validações e re-usabilidade do código não é uma tarefa tão simples.

Formik

Pensando nesses problemas tão comuns na hora de criar formulários, Jared Palmer e Ian White decidiram criar uma biblioteca que facilitasse o gerenciamento de estados dos forms no React (ou React Native), trabalhando com o estado local do componente ao contrário de outras libs que trabalham com o estado global, que acabam adicionando muitas camadas de complexidade e perdendo desempenho.

Além disso, o Formik foi criado para ser simples e organizado, todo o processo de criação de forms com ele é muito rápido e envolve pouca linhas código para se ter um formulário reutilizável e com validação (esse último ponto temos que agradecer também ao Yup, que vamos ver daqui a pouco).

Vamos lá?!

Iremos criar uma aplicação bem simples para demonstrar algumas das diversas features dentro do Formik. Toda estilização fica por sua conta, assim conseguimos focar no mais importante deste artigo.

Estou usando o React Native mas lembre-se que o Yup foi desenvolvido para a biblioteca React, portanto, não está intrinsecamente ligado ao framework RN, logo pode ser utilizado tanto para aplicações Web quanto Mobile.

Antes de qualquer coisa vamos instalar as duas dependências necessárias:

yarn add formik yup

Vamos construir algo que se pareça com isso, já utilizando a estrutura do Formik:

Figura 1 – Estrutura inicial

Para montar a estrutura de um formulário vamos utilizá-los com HOCs (High Order Components).

No exemplo acima, o form não foi escrito dentro da tela que está sendo exibida, neste caso, o mesmo foi criado em outro arquivo para que outras telas possam reutilizá-lo.

Iremos interagir com o formulário (receber os dados, limpar os campos, definir os valores iniciais, etc…) dentro do HOC, enviando os props necessários.

Figura 2 – Exemplo de estrutura

Portanto, nosso arquivo src/Example/index.js, que é a estrutura do formulário ficou dessa forma:

Powered by Rock Convert
import React from 'react';
import {View, StyleSheet, TouchableOpacity, Text} from 'react-native';
import {Formik} from 'formik';
import TextInput from '../../components/TextInput';

function ExampleForm({onSubmit, initialValues, style}) {
  const renderForm = ({
    values,
    setFieldValue,
    setFieldTouched,
    touched,
    errors,
    handleSubmit,
    isValid,
    isSubmitting,
  }) => {
    return (
      <View style={StyleSheet.flatten([styles.container, style])}>
        <TextInput
          onChange={setFieldValue}
          onTouch={setFieldTouched}
          placeholder="Nome"
          name="name"
          value={values.name}
          error={touched.name && errors.name}
        />
        <TextInput
          onChange={setFieldValue}
          onTouch={setFieldTouched}
          keyboardType="email-address"
          autoCapitalize="none"
          placeholder="E-mail"
          name="email"
          value={values.email}
          error={touched.email && errors.email}
        />
        <TextInput
          onChange={setFieldValue}
          onTouch={setFieldTouched}
          placeholder="Senha"
          name="password"
          secureTextEntry={true}
          value={values.senha}
          error={touched.password && errors.password}
        />
        <TextInput
          onChange={setFieldValue}
          onTouch={setFieldTouched}
          placeholder="Confirmar senha"
          name="passwordConfirm"
          secureTextEntry={true}
          value={values.passwordConfirm}
          error={touched.passwordConfirm && errors.passwordConfirm}
        />
        <TouchableOpacity
          disabled={!isValid || isSubmitting}
          onPress={handleSubmit}
          style={StyleSheet.flatten([
            styles.submit,
            !isValid ? styles.submitDisabled : null,
          ])}>
          <Text style={styles.submitText}>Enviar</Text>
        </TouchableOpacity>
      </View>
    );
  };

  return (
    <Formik
      initialValues={initialValues}
      onSubmit={onSubmit}
      render={renderForm}
    />
  );
}

ExampleForm.defaultProps = {
  initialValues: {},
  onSubmit: () => null,
};

const styles = StyleSheet.create({
  container: {},
  submit: {
    height: 50,
    backgroundColor: '#f2b50c',
    justifyContent: 'center',
    alignItems: 'center',
    marginHorizontal: 6,
  },
  submitText: {
    fontSize: 16,
    fontWeight: 'bold',
  },
  submitDisabled: {
    backgroundColor: '#d1cfcf',
  },
});

export default ExampleForm;

Aqui temos um componente funcional que retorna o componente disponibilizado pelo Formik passando algumas props. Vamos dar uma olhada nessas props:

  • initialValues: Essa prop deve ser um objeto que contenha os valores iniciais dos campos no formulário (podemos deixar null se não temos nenhum). Recebemos os valores iniciais (se existirem) do HOC.
  • onSubmit: Aqui passamos o callback do HOC para que quando o usuário envie os valores do form eles sejam passados para o componente acima.
  • render: Responsável por receber o componente do form e o renderizar. Além disso, render envia diversas props para o componente a ser renderizado. Iremos utilizar essas propriedades para construir o formulário na função  renderForm, seguindo os padrões do Formik.

Dentro da função renderForm iremos desestruturar diversas funções e objetos (existem muitos outros, o recomendado é olha na documentação oficial para saber quais outros existem e a funcionalidade de cada um).

Para os Fields (como o Formik os chama), precisamos enviar as props que possibilitam que o componente atualize os estados do form.

Nesse caso, estamos utilizando um componente customizado, o TextInput, que veremos como foi estruturado logo abaixo, e precisamos passar o name, que deve ser único entre os campos, o value que contem o valor atual do field e as funções para setar o valor e blue do input.

src/components/TextInput.js

import React from 'react';
import {View, Text, StyleSheet, TextInput as RNTextInput} from 'react-native';

function TextInput({
  style,
  inputStyle,
  error,
  errorStyle,
  onChange,
  onTouch,
  name,
  ...attributes
}) {
  const onChangeText = text => {
    onChange(name, text);
  };

  const onBlurText = () => {
    onTouch(name);
  };

  return (
    <View style={StyleSheet.flatten([styles.container, style])}>
      <View style={styles.inputContainer}>
        <RNTextInput
          style={StyleSheet.flatten([styles.inputStyle, inputStyle])}
          onChangeText={onChangeText}
          onBlur={onBlurText}
          underlineColorAndroid="transparent"
          {...attributes}
        />
      </View>
      {error ? (
        <Text style={StyleSheet.flatten([styles.error, errorStyle])}>
          {error}
        </Text>
      ) : null}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    paddingVertical: 10,
    paddingHorizontal: 6,
  },
  inputContainer: {
    justifyContent: 'center',
    borderWidth: 1,
    borderColor: 'black',
    paddingHorizontal: 10,
  },
  inputStyle: {
    fontSize: 16,
    height: 50,
  },
  error: {
    marginTop: 8,
    color: 'red',
    fontSize: 15,
  },
});

export default TextInput;

Vale ressaltar que qualquer componente customizado pode ser integrado ao Formik, seja ele um checkbox, radio-button, entre outros, basta integrar o mesmo com os parâmetros citados acima.

Como estamos no RN essa integração dos componentes é um pouco diferente do convencional da Web, caso queira ver as diferenças vale a pena dar uma olhada na documentação oficial.

A implementação do form no arquivo App.js ficou assim:

import React, {useCallback, useState} from 'react';
import {SafeAreaView, StyleSheet, Text} from 'react-native';

import ExampleForm from './src/forms/Example';

const App = () => {
  const [formData, setFormData] = useState(JSON.stringify('{}'));

  const onSubmit = useCallback(async (values, bag) => {
    try {
      setFormData(JSON.stringify(values, null, 2));
    } catch (e) {
      bag.setErrors(e);
    } finally {
      bag.setSubmitting(false);
    }
  }, []);

  return (
    <SafeAreaView style={styles.safeArea}>
      <ExampleForm onSubmit={onSubmit} />
      <Text style={styles.values}>{formData}</Text>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  safeArea: {
    flex: 1,
  },
  values: {
    fontSize: 16,
    marginHorizontal: 6,
    marginTop: 15,
    marginBottom: 40,
  },
});

export default App;

Validando

Outra parte fundamental de qualquer formulário é a possibilidade de validar os campos antes que o usuário os envie para uma API, por exemplo.

Essa implementação fornece uma melhor experiência para o usuário já que o mesmo não vai ter que aguardar a resposta do servidor para validar algo que o próprio client teria capacidade.

Entretanto, é importante ter em mente que a validação client-side é considerada insegura e serve somente o propósito de facilitar o processo para o usuário final. A validação server-side ainda é fundamental.

Vamos criar o arquivo de validação para nosso formulário Exemplo. Dentro do arquivo validation.js iremos utilizar o Yup, uma biblioteca que simplifica infinitamente o processo de criação de regras dos campos, recomendo dar uma boa olhada na documentação e ver as inúmeras features que essa lib oferece.

import * as Yup from 'yup';

export default Yup.object().shape({
  name: Yup.string()
    .min(4, 'Mínimo 4 caracteres')
    .max(20, 'Máximo 20 caracteres'),
  email: Yup.string()
    .email('E-mail inválido')
    .required('E-mail obrigatório'),
  password: Yup.string()
    .min(4, 'Mínimo 4 caracteres')
    .required('Obrigatório'),
  passwordConfirm: Yup.string()
    .min(4, 'Mínimo 4 caracteres')
    .oneOf([Yup.ref('password'), null], 'As senhas não correspondem')
    .required('Obrigatório'),
});

Agora importe o objeto criado pelo Yup no componente do formulário e passe-o como prop no component do Formik.

import validation from './validation';
<Formik
  initialValues={initialValues}
  onSubmit={onSubmit}
  render={renderForm}
  validationSchema={validation}
/>

Pronto! Agora nosso formulário somente estará disponível para ser enviado quando todos os requerimentos definidos no objeto de validação forem satisfeitos.

Finalizando

Apesar de simples nosso formulário funciona brilhantemente. Agora que você já implementou funcionalidades básicas de qualquer formulário vale a pena explorar outras possibilidades de validação com o Yup e outras funcionalidades do Formik.

Qualquer dúvida ou sugestão deixe nos comentário aí embaixo, vamos sempre estar de olho 👀. Obrigado pela leitura.

Entre com seus dados para a ligação.