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

Como fazer uma calculadora em React?

Essa semana decidi fazer um projetinho bem simples em react, uma calculadora. Inicialmente parece ser muito simples (e de fato é), mas sem entender alguns conceitos pode parecer muito difícil. Nesse post vou mostrar as ideias que tive e como fiz para separar as responsabilidades de cada componente dentro da aplicação. Para quem já tem bastante entendimento de react provavelmente não verá nada muito útil, mas fique a vontade para comentar, corrigir alguma coisa ou mandar sugestões, mas para os iniciantes acredito que seja algo muito útil.

A nossa calculadora terá a seguinte cara:

calculadora

Veja que ela é composta por duas partes principais:

  • um prompt que contém o input atual e a expressão completa a ser calculada
  • um teclado com todas as teclas de números e funções da nossa calculadora

Você consegue enxergar os componentes?

Talvez uma das coisas mais difíceis para quem está iniciando na programação é visualizar o código. Não é natural ver algo totalmente abstrato como algo concreto. Mas a capacidade de abstrair as coisas e extrair apenas o essencial é o que "separa os homens dos meninos" na programação.
Cada um abstrai as coisas de uma forma diferente. Na programação não existe receita de bolo, apenas vários caminhos que nos levam para o mesmo lugar, alguns mais longos e outros mais curtos, mas toda solução soluciona o problema.
Nesse caso, temos algo muito simples, podemos "construir" uma calculadora com três componentes:

  • Prompt
  • Keyboard
  • Calculator

De quem é a responsabilidade?

Essa foi a parte que inicialmente eu tive mais dificuldade em fazer as coisas funcionarem, mas o que me ajudou foi simplesmente fazer a seguinte pergunta: "quem deve ser o responsável por isso?". Separar responsabilidades é algo muito importante não apenas para evitar bugs, mas também para ter mais clareza no código. Por exemplo, qual componente deve ser responsável por fazer os cálculos? Qual deles deve conter as funções que implementam as regras de negócio? Para entendermos isso basta olharmos para o que cada componente deve fazer:

  • Prompt
    É o componente que mostra as coisas na tela, sua responsabilidade principal é apenas essa, a sua finalidade é apenas isso e, para evitarmos problemas, seria bom não adicionarmos nenhuma outra responsabilidade.
  • Keyboard
    É o componente que adiciona os botões que contém os números e ações da nossa calculadora na tela, a sua responsabilidade é essa, adicionar os botões na tela, semelhante ao Prompt, seria bom não adicionarmos nenhuma outra responsabilidade nesse componente para termos menos acoplamento no nosso código.
  • Calculator
    É a calculadora em si. Esse componente é o melhor para ter a responsabilidade de implementar as regras de negócio, pois ele engloba tanto o Prompt quanto o Keyboard.

Essa abordagem torna o acoplamento completamente nulo entre Prompt e Keyboard, além de simplificar uma possível manutenção ou adição de novas features.

Falar é fácil, quero ver fazer...

Vamos colocar a mão na massa para implementar esses componentes. Em minha implementação decidi fazer em typescript, no final do post estará o link do projeto e do repositório para você conferir.

// Keyboard.tsx

interface KeyboardProps {
  handleClick: {
   ...
  }
}

export const Keyboard = ({ handleClick }: KeyboardProps) => {
  return (
    <div className="keyboard">
      {buttons.map((button, index) => {
        return (
          <button
            type="button"
            className={button.className}
            id={button.id}
            key={index}
            onClick={e => {
              handleClick[button.className](e.target as HTMLButtonElement)
            }}
          >
            {button.label}
          </button>
        )
      })}
    </div>
  )
}

interface Button {
  label: string
  className: 'number' | 'operation' | 'clear' | 'del' | 'sign' | 'equal' | 'dot'
  id: string
}

const buttons: Button[] = [
  ...
]

Veja como esse como esse componente faz apenas uma coisa: implementa os botões na tela. O que cada botão faz não depende desse componente, pois é uma propriedade passada por um componente pai.

// Prompt.tsx

interface PromptProps {
  content: string
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
  expression: string
}

export const Prompt = ({ content, onChange, expression }: PromptProps) => {
  return (
    <div className="prompt">
      <p className="expression">{expression}</p>
      <input
        type="text"
        value={content}
        onChange={e => {
          onChange(e)
        }}
      />
    </div>
  )
}

Da mesma forma, o componente prompt tem a única responsabilidade de mostrar as coisas na tela. Veja como o valor da expressão e do conteúdo do input não são controlados pelo componente Prompt, pois ele tem apenas a responsabilidade de mostra-los, por isso são apenas propriedades passadas por um componente pai.

// Calculator.tsx

import { useState } from 'react'
import { Keyboard } from './Keyboard'
import { Prompt } from './Prompt'

type Operation = '+' | '-' | '*' | '/' | '%' | ''

export const Calculator = () => {
  const [content, setContent] = useState<string>('')
  const [expression, setExpression] = useState<string>('')
  const [num1, setNum1] = useState<number>(NaN)
  const [operation, setOperation] = useState<Operation>('')

  const handleClick = {
    ...
  }

  return (
    <div className="calculator">
      <Prompt
        content={content}
        onChange={e => {
          setContent(
            e.target.value.match(/[0-9]/)
              ? e.target.value
              : e.target.value.slice(0, -1)
          )
        }}
        expression={expression}
      />
      <Keyboard handleClick={handleClick} />
    </div>
  )
}

Por fim nós temos o componente calculator que é o responsável por integrar teclado e prompt e pelas regras de negócio da nossa aplicação. Perceba como Keyboard e Prompt são completamente independentes entre si e do Calculator, isso nos tráz um ganho muito grande tanto na legibilidade quanto na manutenção do código, pois sabemos exatamente onde encontrar cada coisa.

Mas por que isso funciona?

Tudo isso funciona graças a como o React faz as coisas. Você já se perguntou do porquê ao modificar uma variável nada muda na tela, mas ao utilizar o useState funciona? Isso é muito simples, o useState não muda simplesmente o valor do seu estado, mas renderiza o componente denovo com os novos valores. Isso significa que, ao digitar o número 2000000 na nossa calculadora nós renderizamos o prompt 7 vezes na tela, trocando os valores dos estados content e expression. Como o componente Prompt depende desses estados ele é renderizado novamente também, pois ao mudar o estado de um componente pai, todos os componentes filhos afetados são renderizados novamente também. Por isso tudo funciona mesmo sem o teclado se comunicar diretamente com o prompt.

Lembrar desse conhecimento é a chave para se destravar no React. Muitas vezes apenas complicamos o que é simples e acabamos travando nos nossos estudos ou trabalho. Quando isso acontecer apenas pare, respire e pense em como tudo deveria se comportar e qual deve ser a responsabilidade de cada parte do código.


Calculadora
Repositório

Carregando publicação patrocinada...
1