codev

Início
Loading animation

Quem usa ou já usou Nest deve ter se deparado com o erro _"Nest can't resolve dependencies of the DatabaseService (?). Please make sure that the argument Function at index [0] is available in the DatabaseModule context..."

Há 4 meses

Todo mundo já passou horas pra resolver um problema simples né mesmo. Não é todos os dias que estamos 100%.

Quem usa ou já usou Nest deve ter se deparado com um erro como esse.

🚨 "Nest can't resolve dependencies of the DatabaseService (?). Please make sure that the argument Function at index [0] is available in the DatabaseModule context."

Saiba que pode ser algo simples: o uso de import type no TypeScript!

Eu estou usando o BiomeJS e, no meu caso, ele aplicou automaticamente import type em vários lugares. Isso fez com que o NestJS não reconhecesse as dependências corretamente na injeção de dependência. A solução? Remover import type e desativar essa regra no biome.json.

O problema parece óbvio, mas depende do contexto. Se você já começa o projeto com o BiomeJS e vê esse erro logo no início, a solução pode ser rápida. Ou se você acabou de instalar o BiomeJS, rodou npx @biomejs/biome format --write e sua API que estava funcionando do nada quebra, talvez suspeite mais cedo.

No meu caso, eu usava ESLint e, depois de vários dias desenvolvendo, decidi migrar para o BiomeJS. Rodei o comando de formatação e nenhum erro apareceu. 😂

O problema? Fechei tudo e só voltei a mexer na API dias depois. Quando rodei npm run dev:start de manhã, o erro apareceu na minha cara. Nem passava pela minha cabeça suspeitar do import type que o BiomeJS colocou nos imports. 😂😂😂

Poise, esse foi meu carnaval. 🎉🎉🎉

Arena Sync - Coleta automática de dados de partidas de futebol do campeonato brasileiro

Há 8 meses

Desenvolvi uma aplicação fullstack que envolve coleta de dados automático.

Criei minha API em Nodejs e seu dever é acessar uma API externa periodicamente para buscar dados de partidas de futebol do campeonato brasileiro e além disso, armazenar tais dados no banco SQL e fornecer esses dados para minha aplicação frontEnd.

Criei um mapa relacional, acessando o link abaixo você irá ser direcionado para a plataforma dbdiagram.io.

Mapa relacional no dbdiagram.io

Eu fiz o frontEnd em Reactjs, Nextjs, Javascript/Typescript e Tailwindcss. Como assinei apenas o plano gratuito da API terceira que me fornece os dados das partidas de futebol, tem limitação de tempo, então não pude manter a aplicação em produção, mas gravei um vídeo mostrando tudo de forma resumida. O link que se encontra abaixo leva para esse vídeo.

Projeto fullstack Arena Sync

Zustand e useContext

Há 9 meses

Criei uma formulário para cadastro de produto, com muitos inputs. Decidi reutilizar esse formulário que criar o produto, para poder também atualizar o produto.

Zustand

Eu utilizo zodResolver com useForm no meu formulário:

import { zodResolver } from '@hookform/resolvers/zod' import { useForm } from 'react-hook-form'

Destaquei essa parte para mostrar que no handleProductForm eu chamo o createOrUpdateProduct() que é uma função de dentro do Zustand que dei o nome de useManageProduct()

export const productFormSchema = z.object({   title: z.string().min(1, 'Este campo é obrigatório.'), }) export type ProductFormData = z.infer<typeof productFormSchema> export function ProductForm({ product, technicalProduct }: ProductFormProps) {   const { register, handleSubmit } = useForm<ProductFormData>({     resolver: zodResolver(productFormSchema),   })   const { createOrUpdateProduct, setProductActionType } = useManageProduct()   const { notifyError, notifySuccess } = useNotification()   const pathname = usePathname()   const isUpdateOrCreatePath = pathname === '/product-manage/register-product'   const handleProductForm = async (data: ProductFormData) => {     setProductActionType(isUpdateOrCreatePath)     const result = await createOrUpdateProduct(data)     if (result.success) {       return notifySuccess({ message: result.message, origin: 'server' })     } else {       return notifyError({ message: result.message, origin: 'server' })     }   }   return (     <form       id="product-form"       onSubmit={handleSubmit(handleProductForm)}       className="flex flex-col gap-4 space-y-10"     >        <label className="space-y-2">           <span>Nome</span>           <Input             type="text"             defaultValue={product?.title}             className="bg-transparent"             {...register('title')}           />         </label>    ...

Configuração no useManageProduct() do Zustand.

import { createProduct } from '@/actions/register/products' import { updateProduct } from '@/actions/update/product' import { ProductFormData } from '@/app/(admin)/product-manage/components/product-form/form' import { create } from 'zustand' export interface ManageProduct {   createOrUpdateProduct: (     data: ProductFormData,   ) => Promise<{ success: boolean; message: string }>   setProductActionType: (isNewProduct: boolean) => void   isNewProduct: boolean } export const useManageProduct = create<ManageProduct>((set, get) => ({   isNewProduct: true,     setProductActionType(isNewProduct) {     set({ isNewProduct })   },   async createOrUpdateProduct(dataProduct) {     const { isNewProduct } = get()     if (isNewProduct) {       return await createProduct({ product: dataProduct })     } else {       return await updateProduct({ product: dataProduct })     }   }, }))

Com apenas essa configuração já é capaz de criar ou atualizar um produto utilizando o mesmo formulário.

No código podemos ver que a função createOrUpdateProduct que tem a responsabilidade de passar os dados do produto para a função server side createProduct ou updateProduct.

E para saber para qual função passar esses dados é por meio da função setProductActionType. Ela por sua vez, como vimos no arquivo de ProductForm, recebe o isUpdateOrCreatePath que é um booleano que irá definir se será true ou false, do resultado da comparação de pathname com o trecho da rota '/product-manage/register-product' que é a minha rota de criar produto.

...   const pathname = usePathname()   const isUpdateOrCreatePath = pathname === '/product-manage/register-product'   const handleProductForm = async (data: ProductFormData) => {     setProductActionType(isUpdateOrCreatePath)   ...

E dessa forma a função createOrUpdateProduct irá saber se passa os dados do produto para atualizar ou criar. Se isNewProduct for true é lógico que o administrador quer atualizar, se isNewProduct for false, ele pretende criar um novo produto.

 async createOrUpdateProduct(dataProduct) {     const { isNewProduct } = get()     if (isNewProduct) {       return await createProduct({ product: dataProduct })     } else {       return await updateProduct({ product: dataProduct })     }   },

useContext

Uma alternativa ao Zustand é utilizar o useContext, que também funcionará, porém exigirá mais código. Escrever mais código não é algo negativo por si só, mas é importante avaliarmos cuidadosamente se, no nosso caso, essa abordagem trará benefícios suficientes.

'use client' import { createProduct } from '@/actions/register/products' import { updateProduct } from '@/actions/update/product' import { ProductFormData } from '@/app/(admin)/product-manage/components/product-form/form' import { ReactNode, createContext, useState } from 'react' interface ManageProductFormContextType {   createOrUpdateProduct: (     data: ProductFormData,   ) => Promise<{ success: boolean; message: string }>   setProductActionType: (isNewProduct: boolean) => void } interface ManageProductFormContextProps {   children: ReactNode } export const ManageProductFormContext = createContext(   {} as ManageProductFormContextType, ) export function ManageProductFormContextProvider({   children, }: ManageProductFormContextProps) {   const [isNewProduct, setIsNewProduct] = useState(true)   const setProductActionType = (isNewProduct: boolean) => {     setId(isNewProduct)   }   const createOrUpdateProduct = async (dataProduct: ProductFormData) => {     if (isNewProduct) {       return await createProduct({ product: dataProduct })     } else {       return await updateProduct({ product: dataProduct })     }   }   return (     <ManageProductFormContext.Provider       value={{ createOrUpdateProduct, captureProductId }}     >       {children}     </ManageProductFormContext.Provider>   ) }

É importante mencionar que, ao utilizar useContext, é necessário envolver esse contexto que foi criado, por volta do {children} no seu arquivo layout se caso estiver usando Next.js.

 <ManageProductFormContextProvider>     {children}  </ManageProductFormContextProvider>

Ou envolver seu contexto por volta de suas rotas se caso estiver usando React.js com Vite:

 <ManageProductFormContextProvider>     <Routes />  </ManageProductFormContextProvider>

É importante saber que tem como chegar ao mesmo resultado sem utilizar nem o Zustand ou useContext, apenas fazendo uma lógica simples no próprio componente form para saber qual função enviar os dados do produto. O intuito aqui foi mostrar outras alternativas, e que em caso de adicionar mais complexidade é indicado fazer uso de umas dessas duas abordagem para que fique mais organizado.

SOLID - Conjunto de princípios para design de software que promove a criação de sistemas modulares e de fácil manutenção. Inclui Princípio da Responsabilidade Única, Princípio da Abstração, e outros.

Há 10 meses

SOLID é um acrônimo que representa um conjunto de cinco princípios fundamentais para o design de software orientado a objetos. Esses princípios foram introduzidos por Robert C. Martin e têm como objetivo promover a criação de sistemas modulares, flexíveis e de fácil manutenção. Abaixo, uma visão geral de cada princípio:

  1. Single Responsibility Principle (SRP) - Princípio da Responsabilidade Única

    • Definição: Uma classe ou função deve ter apenas uma única responsabilidade, ou seja, deve fazer uma única coisa e fazer bem.
    • Objetivo: Facilitar a manutenção e a evolução do código, evitando que mudanças em uma responsabilidade afetem outras partes do sistema.
    • Exemplo: Uma classe OrderService que apenas coordena a criação de pedidos, enquanto a lógica de notificação e persistência é delegada a outras classes.
  2. Open/Closed Principle (OCP) - Princípio do Aberto/Fechado

    • Definição: Classes devem ser abertas para extensão, mas fechadas para modificação.
    • Objetivo: Permitir que o comportamento do sistema seja estendido sem alterar o código existente, minimizando o risco de introduzir bugs.
    • Exemplo: Utilizar interfaces e herança para adicionar novas funcionalidades sem modificar as classes já implementadas.
  3. Liskov Substitution Principle (LSP) - Princípio da Substituição de Liskov

    • Definição: Subclasses devem ser substituíveis por suas classes base sem alterar a corretude do programa.
    • Objetivo: Garantir que o sistema funcione corretamente mesmo quando as subclasses são usadas no lugar das classes base.
    • Exemplo: Se uma classe Bird tem um método fly, qualquer classe que herda de Bird, como Sparrow, deve ser capaz de executar fly de maneira consistente.
  4. Interface Segregation Principle (ISP) - Princípio da Segregação de Interfaces

    • Definição: Módulos não devem ser obrigados a depender de interfaces que não utilizam.
    • Objetivo: Evitar interfaces "inchadas" e garantir que as classes implementem apenas o que realmente precisam.
    • Exemplo: Em vez de ter uma única interface gigante para operações de banco de dados, criar interfaces menores e específicas, como IReadRepository e IWriteRepository.
  5. Dependency Inversion Principle (DIP) - Princípio da Inversão de Dependência

    • Definição: Dependa de abstrações, não de implementações concretas.
    • Objetivo: Reduzir o acoplamento entre os componentes, facilitando a troca de implementações e promovendo a flexibilidade do sistema.
    • Exemplo: Injetar uma interface INotificationService em vez de uma classe concreta, permitindo mudar a forma de notificação (e-mail, SMS, etc.) sem alterar o código do cliente.

Resumo

Aplicar os princípios do SOLID ajuda a criar software que é:

  • Modular: Facilmente dividido em partes menores, independentes e coesas.
  • Manutenível: Mais fácil de modificar, estender e corrigir.
  • Testável: Classes e funções são menores e mais focadas, tornando-as mais fáceis de testar.
  • Flexível: O sistema pode evoluir e incorporar novas funcionalidades com menor risco de quebra.

Esses princípios são fundamentais para desenvolver software escalável e sustentável a longo prazo.

Domain-Driven Design (DDD)

Há 10 meses

O que é o domínio?

O domínio é o núcleo do negócio em que você está trabalhando. Ele é composto por um conjunto de ideias, conhecimentos e processos específicos do negócio.

Linguagem Ubíqua

A Linguagem Ubíqua é a terminologia usada no dia a dia da empresa, refletindo a realidade do negócio. O objetivo é garantir que desenvolvedores e especialistas do domínio compartilhem uma compreensão comum e consistente.

Exemplos de Nomenclatura:

  • Em vez de usar apenas "usuário", você pode encontrar termos como "Cliente", "Fornecedor", "Atendente" e "Barman", dependendo do contexto do negócio.

Três Pilares Principais do DDD:

  1. Linguagem Ubíqua

    • Exemplo de Glossário:

      • Playback: Processo de execução de um vídeo.
      • Vídeo: Conteúdo que será visto pelo usuário.
      • Minha Lista: Lista de vídeos que o usuário deseja assistir mais tarde.
    • Após definir a Linguagem Ubíqua, ela deve ser usada consistentemente em todo o projeto.

  2. Bounded Contexts (Contextos Delimitados)

    • Delimita as diferentes áreas da aplicação, cada uma com responsabilidades claramente definidas.
    • Cada contexto pode ter sua própria Linguagem Ubíqua.
    • A definição dos contextos é facilitada através de histórias e colaboração com o especialista de domínio.

    Exemplos de Contextos:

    • Criar uma Conta: "Conta" é a entidade e "Criar" é a ação.
    • Fazer o Login: "Login" é a entidade e "Fazer" é a ação.

    Modelagem Estratégica:

    • Definição dos contextos utilizados no sistema, como "Vídeo", "Minha Lista" e "Autenticação".
    • Exemplo de Divisão dos Domínios:
      • Vídeo: Principal
      • Pagamento: Genérico
      • Autenticação: Auxiliar
  3. Context Map (Mapa de Contextos)

    • O Context Map ajuda a visualizar como os diferentes contextos se relacionam entre si.
    • É uma representação gráfica dos contextos e suas interações

![[contextmap.png]] ![[arquiteruracontextual.png]]

Domain Model Patterns:

  • Objetos de Valor (Value Objects):

    • São imutáveis e representam uma coleção de atributos.
    • Normalmente, não possuem métodos setters e são inicializados via construtor.
    • Utilizam tipagem forte em vez de dados primitivos.
  • Repositórios:

    • Acesso direto à camada de dados para persistir e consultar entidades.
    • Possuem métodos para agregar e acessar dados, podendo também interagir com serviços externos.
  • Serviços de Domínio:

    • Implementam a lógica de negócios com base na definição do especialista de domínio.
    • Trabalham com fluxos complexos de diversas entidades e agregações.
    • Utilizam repositórios para acesso a dados e consomem recursos da camada de infraestrutura, como enviar e-mails ou disparar eventos.

![[concat.png]]

Aplicar DDD permite criar um software mais alinhado com as necessidades do negócio, com uma arquitetura clara e uma comunicação eficaz entre os desenvolvedores e especialistas do domínio.

Divisão de Responsabilidades - Single Responsibility Principle (SRP)

Há 10 meses

O Princípio da Responsabilidade Única (SRP) é um dos princípios do SOLID que afirma que uma classe ou função deve ter apenas uma única responsabilidade, ou seja, ela deve fazer uma única coisa e fazê-la bem. Isso torna o código mais fácil de manter, testar e expandir.

Cenário

Você está desenvolvendo uma aplicação de gerenciamento de pedidos. Inicialmente, você tem uma classe OrderService que faz várias coisas: cria um pedido, envia uma notificação ao usuário, e salva o pedido no banco de dados. Essa classe está violando o SRP porque tem múltiplas responsabilidades.

Classe Antes de Aplicar o SRP

class OrderService { createOrder(orderData) { // Lógica para criar o pedido const order = this.processOrder(orderData); // Enviar notificação ao usuário this.sendNotification(order); // Salvar pedido no banco de dados this.saveToDatabase(order); return order; } processOrder(orderData) { // Lógica para processar o pedido } sendNotification(order) { // Lógica para enviar notificação ao usuário } saveToDatabase(order) { // Lógica para salvar o pedido no banco de dados } }

Aqui, a OrderService está responsável por três coisas: processar o pedido, enviar notificações, e salvar no banco de dados. Isso viola o SRP.

Refatorando com SRP

Vamos dividir essas responsabilidades em classes separadas:

// Classe para processar o pedido class OrderProcessor { process(orderData) { // Lógica para processar o pedido return { /* dados do pedido processado */ }; } } // Classe para enviar notificações class NotificationService { send(order) { // Lógica para enviar notificação ao usuário } } // Classe para salvar no banco de dados class OrderRepository { save(order) { // Lógica para salvar o pedido no banco de dados } } // Classe principal que agora tem apenas a responsabilidade de orquestrar as operações class OrderService { constructor() { this.orderProcessor = new OrderProcessor(); this.notificationService = new NotificationService(); this.orderRepository = new OrderRepository(); } createOrder(orderData) { const order = this.orderProcessor.process(orderData); this.notificationService.send(order); this.orderRepository.save(order); return order; } }

Benefícios de Aplicar o SRP

  • Facilidade de Manutenção: Cada classe tem uma responsabilidade clara e única, tornando mais fácil entender e modificar o código.
  • Testabilidade: Com responsabilidades separadas, fica mais fácil testar cada parte individualmente.
  • Reutilização: Componentes como NotificationService ou OrderRepository podem ser reutilizados em outras partes da aplicação ou em outros projetos.

Ao aplicar o SRP, você torna o código mais modular, o que facilita sua manutenção e expansão ao longo do tempo.

SCSS-Sass - Pré-processador CSS que adiciona funcionalidades como variáveis e aninhamento

Há 10 meses

Definição: Sass (Syntactically Awesome Style Sheets) é um pré-processador CSS que estende as capacidades do CSS tradicional, adicionando funcionalidades como variáveis, aninhamento, mixins, funções e muito mais. SCSS é a sintaxe mais recente do Sass, que mantém a compatibilidade com a sintaxe CSS padrão, facilitando a adoção em projetos existentes.

Por que usar?

  • Variáveis: Permitem a definição de valores reutilizáveis, como cores, fontes e espaçamentos, facilitando a manutenção e consistência do design.
  • Aninhamento: Melhora a legibilidade e organização do código, permitindo estruturar estilos de forma hierárquica e refletir a estrutura HTML.
  • Mixins e Funções: Reutilizam blocos de código e implementam lógica dentro do CSS, reduzindo redundâncias e promovendo a eficiência.
  • Modularidade: Facilita a divisão do CSS em múltiplos arquivos, melhorando a organização e a escalabilidade do projeto.

Exemplo prático:

Vamos criar um componente de cartão de usuário (UserCard) utilizando SCSS em um projeto Next.js com React.

  1. Configuração do Next.js para usar SCSS:

Next.js suporta SCSS nativamente. Basta instalar as dependências necessárias:

npm install sass
  1. Criar o arquivo SCSS para o componente:
/* components/UserCard.module.scss */ $primary-color: #0070f3; $secondary-color: #005bb5; $background-color: #f9f9f9; $text-color: #333; $padding: 16px; $border-radius: 8px; $box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); .card { background-color: $background-color; border: 1px solid #ddd; border-radius: $border-radius; padding: $padding; max-width: 300px; box-shadow: $box-shadow; transition: box-shadow 0.3s; &:hover { box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); } .username { font-size: 1.5rem; color: $text-color; margin-bottom: 8px; } .email { font-size: 1rem; color: darken($text-color, 20%); } .button { margin-top: 12px; padding: 10px 20px; background-color: $primary-color; color: white; border: none; border-radius: 4px; cursor: pointer; transition: background-color 0.3s; &:hover { background-color: $secondary-color; } &.disabled { background-color: #ccc; cursor: not-allowed; } } }

Explicação do SCSS:

  • Variáveis: Definimos várias variáveis no início para cores, espaçamentos e outros valores reutilizáveis.
  • Aninhamento: Utilizamos o aninhamento para estruturar os estilos de .username, .email e .button dentro da classe .card.
  • Pseudo-classes: Aplicamos estilos para :hover dentro da classe .card e .button para interatividade.
  • Modificadores de Classe: A classe .button.disabled altera o estilo do botão quando está desabilitado.
  1. Criar o componente React utilizando o CSS Module:
// components/UserCard.js import styles from './UserCard.module.scss'; import PropTypes from 'prop-types'; export default function UserCard({ name, email, disabled }) { return ( <div className={styles.card}> <h2 className={styles.username}>{name}</h2> <p className={styles.email}>{email}</p> <button className={`${styles.button} ${disabled ? styles.disabled : ''}`} disabled={disabled} > {disabled ? 'Indisponível' : 'Contato'} </button> </div> ); } UserCard.propTypes = { name: PropTypes.string.isRequired, email: PropTypes.string.isRequired, disabled: PropTypes.bool, }; UserCard.defaultProps = { disabled: false, };
  1. Utilizar o componente no aplicativo:
// pages/index.js import UserCard from '../components/UserCard'; export default function Home() { const users = [ { id: 1, name: 'João Silva', email: 'joao.silva@example.com' }, { id: 2, name: 'Maria Oliveira', email: 'maria.oliveira@example.com' }, { id: 3, name: 'Pedro Santos', email: 'pedro.santos@example.com', disabled: true }, ]; return ( <div style={{ display: 'flex', gap: '20px', padding: '20px' }}> {users.map((user) => ( <UserCard key={user.id} name={user.name} email={user.email} disabled={user.disabled} /> ))} </div> ); }

Explicação do exemplo:

  • Variáveis: Utilizamos variáveis SCSS para definir cores e espaçamentos, facilitando a alteração de temas e a consistência visual.
  • Aninhamento: Estruturamos os estilos do componente UserCard de forma hierárquica, refletindo a estrutura do HTML.
  • Modularidade: Cada componente possui seu próprio arquivo SCSS (UserCard.module.scss), garantindo escopo local e evitando conflitos de estilos.
  • Interatividade: Adicionamos efeitos de hover e estados desabilitados para melhorar a experiência do usuário.

Vantagens:

  • Manutenção Simplificada: Variáveis e mixins permitem atualizações rápidas e consistentes em todo o projeto.
  • Organização: O aninhamento e a modularidade promovem um código mais limpo e fácil de entender.
  • Reutilização: Componentes estilizados com SCSS podem ser facilmente reutilizados em diferentes partes da aplicação.
  • Escalabilidade: SCSS facilita a gestão de grandes bases de código CSS, tornando o desenvolvimento mais eficiente à medida que o projeto cresce.

Conclusão: SCSS/Sass é uma ferramenta poderosa que aprimora a escrita de CSS, oferecendo funcionalidades avançadas que aumentam a produtividade e a organização do código. Integrado com frameworks modernos como Next.js e bibliotecas como React, SCSS promove uma estilização modular, escalável e eficiente, atendendo às necessidades de projetos web contemporâneos.

PostCSS - Ferramenta para transformar CSS com plugins, como autoprefixer e minificação

Há 10 meses

Definição: PostCSS é uma ferramenta para transformar CSS utilizando uma variedade de plugins que automatizam e otimizam o processo de escrita de CSS. Com PostCSS, você pode usar plugins para adicionar prefixos específicos do navegador (como -webkit- ou -moz-), minificar o CSS, e integrar frameworks como TailwindCSS.

Por que usar?

  • Automatização: PostCSS automatiza tarefas repetitivas, como adicionar prefixos de navegador ou minificar CSS.
  • Modularidade: Permite adicionar e remover plugins conforme necessário, tornando o build process flexível e adaptável às necessidades do projeto.
  • Performance: Melhora a performance do CSS com plugins de otimização, como minificação, que reduz o tamanho dos arquivos CSS.
  • Integração com TailwindCSS: Facilita a configuração de TailwindCSS em projetos, automatizando a geração de utilitários CSS.

Exemplo prático:

Vamos configurar um projeto Next.js com TailwindCSS e PostCSS, incluindo plugins como autoprefixer para adicionar prefixos automaticamente.

  1. Instalar dependências:

Primeiro, instale o PostCSS, o Autoprefixer e o plugin de minificação:

npm install tailwindcss postcss autoprefixer
  1. Configurar o PostCSS:

Crie um arquivo postcss.config.js na raiz do projeto:

// postcss.config.js module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, }
  • tailwindcss: Este plugin gera os utilitários CSS necessários com base na configuração do Tailwind.
  • autoprefixer: Adiciona prefixos específicos para garantir compatibilidade com diferentes navegadores.
  1. Configuração do TailwindCSS:

Crie o arquivo de configuração do Tailwind (tailwind.config.js):

// tailwind.config.js module.exports = { content: [ './pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}', ], theme: { extend: {}, }, plugins: [], }
  1. Configuração dos estilos globais:

No arquivo globals.css, importe o TailwindCSS:

/* styles/globals.css */ @tailwind base; @tailwind components; @tailwind utilities;

Explicação:

  • @tailwind base; Importa estilos base como resets CSS.
  • @tailwind components; Importa componentes pré-estilizados que podem ser reutilizados.
  • @tailwind utilities; Importa os utilitários gerados pelo TailwindCSS, como classes de espaçamento, cores, etc.
  1. Exemplo de uso no componente:

Vamos criar um componente de botão estilizado com TailwindCSS.

// components/Button.js export default function Button({ children }) { return ( <button className="bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-700"> {children} </button> ); }
  1. Utilizar o componente no aplicativo:
// pages/index.js import Button from '../components/Button'; export default function Home() { return ( <div className="flex justify-center items-center h-screen"> <Button>Click Me</Button> </div> ); }

Explicação do exemplo:

  • TailwindCSS: Usamos classes utilitárias do TailwindCSS diretamente no componente Button para estilizar o botão de maneira rápida e eficiente.
  • PostCSS com Autoprefixer: Quando o CSS é processado, autoprefixer adiciona automaticamente os prefixos de navegador necessários para garantir a compatibilidade.
  • Modularidade e Simplicidade: PostCSS permite configurar apenas os plugins necessários, mantendo o processo de build eficiente e fácil de gerenciar.

Vantagens:

  • Flexibilidade: Adicione ou remova plugins PostCSS conforme as necessidades do projeto evoluem.
  • Compatibilidade: autoprefixer garante que o CSS funcione corretamente em uma ampla gama de navegadores.
  • Otimização: Minificação automática e outras otimizações podem ser facilmente integradas ao fluxo de trabalho.
  • Integração Simples: A configuração com TailwindCSS é direta, proporcionando uma experiência de desenvolvimento rápida e fluida.

Conclusão: PostCSS é uma ferramenta poderosa e versátil que simplifica o processo de desenvolvimento CSS, especialmente quando combinado com frameworks como TailwindCSS. Ele automatiza tarefas importantes, melhora a compatibilidade entre navegadores e ajuda a manter o código CSS limpo e otimizado.

Framework CSS TailwindCSS - Framework utilitário para estilização rápida e responsiva

Há 10 meses

Definição: TailwindCSS é um framework CSS utilitário que permite aplicar estilos diretamente em classes HTML, promovendo uma estilização rápida e responsiva. Ele se destaca por sua abordagem de "Atomic CSS", onde cada classe faz apenas uma coisa, tornando a composição de estilos extremamente flexível.

Por que usar?

  • Produtividade: Você estiliza componentes rapidamente sem sair do HTML/JSX.
  • Customização: Fácil de personalizar e adaptar para atender às necessidades específicas do design.
  • Responsivo: Classes utilitárias para design responsivo, garantindo que sua aplicação funcione bem em diferentes dispositivos.

Exemplo prático:

Vamos criar uma seção de cards responsivos que exibem informações de produtos:

// pages/index.js export default function Home() { const products = [ { id: 1, name: "Produto A", price: "$20" }, { id: 2, name: "Produto B", price: "$30" }, { id: 3, name: "Produto C", price: "$40" }, ]; return ( <div className="container mx-auto px-4 py-8"> <h1 className="text-2xl font-bold text-center mb-6">Produtos</h1> <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"> {products.map((product) => ( <div key={product.id} className="bg-white rounded-lg shadow-md p-4 hover:shadow-xl transition-shadow" > <h2 className="text-xl font-semibold">{product.name}</h2> <p className="text-gray-700">{product.price}</p> <button className="mt-4 bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600"> Comprar </button> </div> ))} </div> </div> ); }

Explicação do exemplo:

  • Grid Responsivo: A classe grid em conjunto com grid-cols-1 sm:grid-cols-2 md:grid-cols-3 ajusta o layout dos cards conforme a largura da tela, criando uma grade responsiva.
  • Utilitários para Espaçamento e Cores: Classes como px-4, py-8, bg-white, text-gray-700, bg-blue-500 são exemplos de como o Tailwind permite aplicar rapidamente espaçamento, cores e outros estilos utilitários.
  • Transições e Efeitos: Classes como hover:shadow-xl e transition-shadow adicionam interatividade ao card, criando uma sombra maior ao passar o mouse.

Vantagens:

  • Consistência: Usando classes pré-definidas, você mantém um estilo consistente sem escrever CSS personalizado.
  • Velocidade: Reduz o tempo gasto na estilização de componentes, permitindo que você se concentre mais na lógica e funcionalidade.
  • Personalização: Tailwind permite a customização de suas classes utilitárias através de um arquivo de configuração, para que você possa ajustar o design às necessidades do projeto.

Conclusão: TailwindCSS facilita a criação de interfaces modernas e responsivas, oferecendo uma abordagem altamente eficiente e personalizável para estilização, sem precisar sair do fluxo de desenvolvimento.

CSS-in-JS Styled-components - Biblioteca que permite escrever CSS dentro de arquivos JavaScript-TypeScript

Há 10 meses

CSS-in-JS com Styled-components

Definição: Styled-components é uma biblioteca para React e React Native que permite escrever estilos CSS diretamente dentro de arquivos JavaScript ou TypeScript. Isso promove uma melhor organização do código, mantendo os estilos associados diretamente aos componentes.

Por que usar?

  • Escopo local: Os estilos aplicados a um componente não vazam para outros.
  • Dinâmico: Permite alterar estilos com base em propriedades e estado dos componentes.
  • Manutenção fácil: Estilos e componentes ficam juntos, facilitando a manutenção e leitura do código.

Exemplo prático:

Vamos criar um botão estilizado que muda de cor ao passar o mouse (hover) e quando está desabilitado:

// Importando a biblioteca import styled from 'styled-components'; // Criando um componente de botão com estilos dinâmicos const Button = styled.button` background-color: ${(props) => (props.disabled ? '#ccc' : '#0070f3')}; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')}; transition: background-color 0.3s; &:hover { background-color: ${(props) => !props.disabled && '#005bb5'}; } `; // Utilizando o botão no componente React export default function App() { return ( <div> <Button>Ativo</Button> <Button disabled>Desabilitado</Button> </div> ); }

Explicação do exemplo:

  • Props Dinâmicas: O botão usa a prop disabled para decidir a cor de fundo e o comportamento do cursor.
  • Transições: O estilo do botão muda suavemente ao passar o mouse, mas apenas se o botão não estiver desabilitado.
  • Reuso e Manutenção: Esse componente é reutilizável e fácil de manter, pois todo o estilo está encapsulado.

Vantagens:

  • Reuso de estilos: Pode-se reutilizar os estilos facilmente em diferentes partes do projeto.
  • Sem conflitos de CSS: Garantia de que os estilos aplicados a um componente não afetarão outros componentes.
  • Tema Global: Com Styled-components, é possível criar temas globais para manter uma consistência visual em toda a aplicação.

Com Styled-components, você combina o poder do CSS com a flexibilidade do JavaScript, criando interfaces mais robustas e organizadas.

CSS Modules - Estilização modular onde cada componente tem seu próprio arquivo CSS, evitando conflitos de nomes

Há 10 meses

Definição: CSS Modules é uma abordagem de estilização em que cada componente tem seu próprio arquivo CSS, garantindo que os estilos sejam aplicados apenas ao componente correspondente. Isso evita conflitos de nomes e promove uma melhor organização do código.

Por que usar?

  • Escopo Local: As classes são automaticamente escopadas ao componente, eliminando o risco de conflitos com outras partes da aplicação.
  • Modularidade: Facilita a manutenção, pois cada componente gerencia seus próprios estilos.
  • Simplicidade: Mantém a sintaxe CSS padrão, sem a necessidade de aprender novas convenções.

Exemplo prático:

Vamos criar um componente de cartão de usuário (UserCard) com CSS Modules:

  1. Crie o arquivo CSS Module:
/* UserCard.module.css */ .card { background-color: #f9f9f9; border: 1px solid #ddd; border-radius: 8px; padding: 16px; max-width: 300px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .username { font-size: 1.5rem; color: #333; } .email { font-size: 1rem; color: #555; }
  1. Utilize o CSS Module no componente:
// UserCard.js import styles from './UserCard.module.css'; export default function UserCard({ name, email }) { return ( <div className={styles.card}> <h2 className={styles.username}>{name}</h2> <p className={styles.email}>{email}</p> </div> ); }
  1. Componente em uso:
// pages/index.js import UserCard from '../components/UserCard'; export default function Home() { return ( <div> <UserCard name="John Doe" email="john.doe@example.com" /> </div> ); }

Explicação do exemplo:

  • Escopo Local: As classes card, username, e email no arquivo UserCard.module.css são automaticamente transformadas em nomes únicos, evitando conflitos de estilo com outros componentes.
  • Simplicidade: Continuamos a usar CSS normal, mas com a segurança de que os estilos não vazam para outros componentes.
  • Modularidade: Cada componente gerencia seus próprios estilos, facilitando a manutenção e escalabilidade do projeto.

Vantagens:

  • Evita Conflitos: Não há risco de sobreposição de estilos entre diferentes componentes.
  • Manutenção Fácil: Como os estilos estão diretamente relacionados ao componente, o código é mais fácil de entender e manter.
  • Integração Simples: CSS Modules funciona bem com qualquer configuração de build que suporte módulos, como Webpack, usado por padrão em Next.js.

Conclusão: CSS Modules oferece uma maneira eficiente e organizada de gerenciar estilos em uma aplicação React, promovendo modularidade e evitando conflitos de nomes, sem comprometer a simplicidade da escrita de CSS.

Axios - Biblioteca popular para fazer requisições HTTP

Há 10 meses

1. Configuração Simples

  • Importação e Configuração Base: Importar Axios e definir uma URL base para todas as requisições, simplificando chamadas repetitivas.
import axios from 'axios' export const api = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, headers: { 'Content-Type': 'application/json', }, withCredentials: true, })

File

2. Requisições HTTP

  • GET: Para buscar dados.
import { api } from '@/lib/api' export const getDataCatalog = async () => { try { const response = await api.get('/categories') return { propsCategories: { categories: JSON.stringify(response.data.categories), }, revalidate: 60 * 60 * 24, // 1 day } } catch (err) { return { notFound: true, propsCategories: { categories: '[]' }, } } }

Resumo: Axios facilita a comunicação com APIs, oferecendo uma configuração intuitiva, suporte a interceptadores para manipulação de headers e tratamento de erros, além de uma integração fluida com SSR, essencial para projetos que exigem renderização no servidor.

1 / 3