Extraindo lógica de componentes React

Componentes mais simples, cobertura de testes maior!

Já parou para pensar na quantidade de "ouro" dentro das funções dos seus componentes?

O ponto de partida

class Money extends Component {
static propTypes = {
currency: PropTypes.string.isRequired,
amount: PropTypes.number.isRequired,
}
getCurrencyData(currency) {
return {
GBP: { base: 100, symbol: '£' },
USD: { base: 100, symbol: '$' },
}[this.props.currency]
}
formatAmount(amount, base) {
return parseFloat(amount / base).toFixed(2)
}
render() {
const currency = this.getCurrencyData()
if (currency) {
const { symbol, base } = currency
const formatted = this.formatAmount(this.props.amount, base)
return (
<span>{symbol}{formatted}</span>
)
} else {
return <span>{this.props.amount}</span>
}
}
}

Extraindo a formatação de quantidade

export const formatAmount = (amount, base) => {
return parseFloat(amount / base).toFixed(2)
}
import { formatAmount } from './format-currency'class Money extends Component {
...
formatAmount(amount, base) {
return formatAmount(amount, base)
}
...
}
// render do componente Money
render() {
const currency = this.getCurrencyData()
if (currency) {
const { symbol, base } = currency
// aqui estávamos usando `this.formatAmount`
const formatted = formatAmount(this.props.amount, base)

return (
<span>{symbol}{formatted}</span>
)
} else {
return <span>{this.props.amount}</span>
}
}
// testes para o nosso novo módulo
import { formatAmount } from './format-currency'
test('it formats the amount to 2 dp', () => {
expect(formatAmount(2000, 100)).toEqual('20.00')
})
test('respects the base', () => {
expect(formatAmount(2000, 10)).toEqual('200.00')
})
test('it deals with decimal places correctly', () => {
expect(formatAmount(2050, 100)).toEqual('20.50')
})

Extraindo as informações de moeda

export const getCurrencyData = currency => {
return {
GBP: { base: 100, symbol: '£' },
USD: { base: 100, symbol: '$' },
}[this.props.currency]
}
...
import { getCurrencyData } from './currency-data'
class Money extends Component {
...
getCurrencyData(currency) {
return getCurrencyData(currency)
}
render() {
const currency = this.getCurrencyData(this.props.currency)
...
}
}
render() {
const currency = getCurrencyData(this.props.currency)
...
}
import { getCurrencyData } from './currency-data'test('for GBP it returns the right data', () => {
expect(getCurrencyData('GBP')).toEqual({
base: 100,
symbol: '£',
})
})

Simplificando o componente Money

import React, { Component } from 'react';
import PropTypes from 'prop-types'
import { formatAmount } from './format-currency'
import { getCurrencyData } from './currency-data'
class Money extends Component {
static propTypes = {
currency: PropTypes.string.isRequired,
amount: PropTypes.number.isRequired,
}
render() {
const currency = getCurrencyData(this.props.currency)
if (currency) {
const { symbol, base } = currency
const formatted = formatAmount(this.props.amount, base)
return (
<span>{symbol}{formatted}</span>
)
} else {
return <span>{this.props.amount}</span>
}
}
}
export default Money
import React from 'react';
import PropTypes from 'prop-types'
import { formatAmount } from './format-currency'
import { getCurrencyData } from './currency-data'
const Money = ({ currency, amount }) => {
const currencyData = getCurrencyData(currency)
if (currencyData) {
const { symbol, base } = currencyData
const formatted = formatAmount(amount, base)
return (
<span>{symbol}{formatted}</span>
)
} else {
return <span>{amount}</span>
}
}Money.propTypes = {
currency: PropTypes.string.isRequired,
amount: PropTypes.number.isRequired,
}
export default Money

Conclusão

Créditos

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store