Next.js, a estrutura React da Vercel, continuou a crescer em popularidade à medida que mais desenvolvedores React buscam renderização do lado do servidor, geração de site estático e regeneração estática incremental, entre outros SEO e benefícios de otimização.

Com a curva de aprendizado suave que vem com Next.js, construir aplicativos dentro da estrutura é fácil, mesmo se você só usou React anteriormente.

Para aprender como construir um aplicativo Next.js, este tutorial detalha como construir um aplicativo da web de carrinho de compras para uma loja de jogos fictícia com a capacidade de adicionar ou remover itens do carrinho, visualizar todos os produtos, visualizar produtos por categoria e muito mais.

O que este tutorial Next.js cobre

Ao construir o aplicativo, vamos cobrir os seguintes recursos:

Configurando um projeto Next.js com create-next-app O sistema de roteamento em Next.js Estilo com módulos CSS Otimização de imagens ização com o componente Integrando Redux Toolkit para gerenciamento de estado global Geração estática e renderização do lado do servidor Next.js API roteia Busca de dados com getStaticProps (), getStaticPaths e getServerSideProps ()

Você pode encontrar o código-fonte para o projeto concluído neste repositório GitHub e o live demo implantado no Vercel .

Primeiros passos com create-next-app

Para criar um novo aplicativo Next.js usando criar-next-app, execute o seguinte comando no terminal e espere o processo de instalação terminar:

npx create-next-app shopping-cart

Depois de terminar, inicie o servidor de desenvolvimento executando o npm run dev roteiro. Por padrão, a estrutura de pastas do aplicativo Next.js se parece com o seguinte:

|-node_modules |-package.json |-package-lock.json |-pages | |-api | | |-hello.js | |-_app.js | |-index.js |-public | |-favicon.ico | |-vercel.svg |-README.md |-styles |-globals.css |-Home.module.css

Para começar sem nenhum estilo existente, podemos limpar o código clichê inicial removendo os estilos de styles/globals.css e styles/Home.module.css e substituindo o código dentro de pages/index.js por um componente funcional React simples, como este:

const HomePage=()=> {return (

Carrinho de compras

); }; exportar HomePage padrão;

Além disso, podemos colocar todos os estilos globais dentro do arquivo styles/global.css. Esses estilos serão aplicados em todo o aplicativo. Por enquanto, vamos adicionar alguns estilos básicos e importar a fonte Open Sans do Google Fonts :

@ import url (‘https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&display=swap’); *, *:: antes, *:: depois {margem: 0; preenchimento: 0; dimensionamento da caixa: caixa de borda; } body {font-family:’Open Sans’, sans-serif; }

Ao longo deste projeto, estaremos construindo quatro páginas diferentes:

Uma página inicial com uma página de destino que mostra todas as categorias disponíveis Uma página de categoria que mostra todos os produtos de uma determinada categoria Uma página de loja que mostra todos produtos de todas as categorias Uma página do carrinho gerencia todos os itens do carrinho

Mas, antes de construir essas páginas, vamos construir alguns componentes comuns, como a barra de navegação e o rodapé.

Componentes da barra de navegação e rodapé

Para começar a adicionar os componentes Navbar e Footer, crie uma nova pasta chamada componentes dentro da pasta raiz do projeto. Isso mantém todos os nossos componentes em um só lugar e mantém nosso código organizado.

Dentro da pasta, crie um novo arquivo chamado Navbar.js ou Navbar.jsx, de acordo com sua preferência:

import Link from’próximo/link’; const Navbar=()=> {return (

); }; exportar Navbar padrão;

A tag do próximo/link permite que os usuários naveguem por diferentes páginas do aplicativo. Isso é semelhante à tag do react-router-dom usada nos aplicativos React.

Mas, em vez de usar o to prop, a tag Next.js exige que passemos o href prop.

Estilo da barra de navegação e rodapé

Para definir o estilo da barra de navegação, crie um novo arquivo chamado Navbar.module.css dentro da pasta de estilos e cole os seguintes estilos dentro do arquivo. Sinta-se à vontade para alterar os estilos de acordo com sua preferência:

.navbar {display: flex; justify-content: espaço entre; alinhar-itens: centro; preenchimento: 2rem; }.logo {tamanho da fonte: 1.2rem; peso da fonte: 600; transformação de texto: maiúsculas; }.links {display: flex; }.navlink {estilo de lista: nenhum; margem: 0 0,75 rem; transformação de texto: maiúsculas; }.navlink a {text-decoration: none; cor preta; }.navlink a: hover {color: # f9826c; }

Agora, para usar os estilos dentro do componente, importe o módulo CSS e adicione className ao JSX. Modifique o código dentro do Navbar.jsx e faça as seguintes alterações:

import Link from’next/link’; importar estilos de’../styles/Navbar.module.css’; const Navbar=()=> {return (

); }; exportar Navbar padrão;

Com o componente Navbar concluído, vamos prosseguir e construir o componente Rodapé.

Crie um novo arquivo chamado Footer.jsx dentro da pasta de componentes:

importe estilos de’Footer.module. css’; const Footer=()=> {return (

Copyright GamesKart {”} {new Date (). getFullYear ()}

); };

Em seguida, adicione Footer.module.css dentro da pasta de estilos:

.footer {padding: 1rem 0; cor preta; alinhamento de texto: centro; }.brand {color: # f9826c; }

Finalmente, importe os componentes Navbar e Footer dentro de pages/_app.js para que fiquem visíveis em todas as páginas do aplicativo. Como alternativa, podemos importar esses componentes para cada página individualmente em que desejamos exibi-los:

import Navbar de’../components/Navbar’; importar rodapé de’../components/Footer’; import’../styles/globals.css’; função MyApp ({Component, pageProps}) {return (

); } exportar MyApp padrão;

Para visualizar nossos componentes em ação, vá para http://localhost: 3000 após executar o script dev run npm. No entanto, todos os componentes são espremidos no topo da página.

Isso pode ser corrigido envolvendo nosso conteúdo dentro de um flex container definido como coluna e justificando o conteúdo no eixo principal em um espaço entre a moda. Basta adicionar uma nova classe ao div na linha 7 de _app.js:

//Linha 7

Em seguida, estilize-o globalmente dentro de globals.css:

//Adicione este código na parte inferior de globals.css.wrapper {min-height: 100vh; display: flex; direção flexível: coluna; justify-content: espaço entre; }

E é isso, corrigimos o problema.

Vamos passar para a construção da página inicial.

Criação da página inicial

Na página inicial, haverá cinco cartas exibidas para as categorias de produtos Xbox, PS5, Switch, PC e acessórios. Vamos começar criando um componente CategoryCard.

Crie dois novos arquivos chamados CategoryCard.jsx dentro da pasta de componentes e CategoryCard.module.css dentro da pasta de estilos.

Cole o seguinte código dentro o arquivo CategoryCard.jsx:

import Link from’next/link’; importar imagem de’próxima/imagem’; importar estilos de’../styles/CategoryCard.module.css’; const CategoryCard=({image, name})=> {return (

{name}

COMPRE AGORA

); }; exportar CategoryCard padrão;

Este componente leva dois adereços: a imagem que é exibida e o nome da categoria. O componente é integrado ao Next.js para fornecer otimização de imagem.

Cole a próxima sequência de código dentro do arquivo CategoryCard.module.css:

.card {margin: 0.5rem; flex: 1 1 automático; posição: relativa; }.image {ajuste ao objeto: capa; borda: 2px totalmente preto; transição: todos os 5s cúbicos-bézier (0,14, 0,96, 0,91, 0,6); }.info {posição: absoluta; topo: 50%; esquerda: 50%; fundo: branco; preenchimento: 1,5 rem; alinhamento de texto: centro; transformar: traduzir (-50%,-50%); opacidade: 0,8; borda: 1px totalmente preto; cursor: ponteiro; }.card: hover.image {transform: scale (1.2); }.card: hover.info {opacity: 0.9; }

Importe o componente CategoryCard dentro da página index.js, que é nossa página inicial, para testar nosso componente recém-criado:

import CategoryCard de’../components/CategoryCard’; importar estilos de’../styles/Home.module.css’; const HomePage=()=> {return (

); }; exportar HomePage padrão;

Para estilização, adicione o código CSS a Home.module.css na pasta de estilos:

.container {padding: 0 2rem; }.small {display: grid; colunas-modelo de grade: 1fr 1fr 1fr; }.large {display: grid; colunas de modelo de grade: 1fr 1fr; }

Embora tenhamos adicionado o estilo, fomos recebidos por um erro:

Corrigir esse problema é simples e mensagem de erro contém um link para a documentação Next.js para explicar e corrigir o erro.

No entanto, para este tutorial, crie um novo arquivo chamado next.config.js dentro da pasta raiz do projeto e adicione o seguinte código:

module.exports={images: {domains: [‘imgur.com’],},};

Adicionando imagens

Como estamos usando imgur.com para hospedar nossas imagens, devemos adicioná-las à matriz de domínios para serem otimizados, garantindo que URLs externos não sejam abusados.

Quando terminar, reinicie o servidor de desenvolvimento para iniciar as alterações. Com isso, construímos com sucesso a página inicial.

Construindo uma API com rotas de API Next.js

Antes de prosseguir para as outras páginas, devemos construir uma API para buscar os produtos de. Embora Next.js seja uma estrutura React, podemos tirar proveito de seu recurso de rotas de API integrado para construir uma API simples.

Precisamos de duas rotas de API para este projeto:

/api/products para buscar os produtos de/api/products/ para buscar produtos pertencentes a uma determinada categoria

Na estrutura de pastas de nosso aplicativo Next.js, há uma pasta chamada api dentro da pasta pages. Exclua todos os arquivos existentes dentro da pasta api, pois iremos construir o nosso do zero.

Para manter as coisas simples, iremos armazenar todos os produtos da loja em um arquivo JSON chamado data.json.

No entanto, você pode usar um banco de dados ou um CMS sem comando com Next.js para adicionar, editar ou excluir produtos sem um arquivo JSON.

Crie uma nova pasta chamada produtos dentro da pasta api, vá para a pasta produtos e crie três arquivos: index.js, [categoria].js e data.json.

index.js

O primeiro arquivo com o qual trabalharemos é index.js, que corresponde ao/api/products direciona e busca todos os produtos em todas as categorias:

import data from’./data.json’; função de exportação getProducts () {dados de retorno; } exportar manipulador de função padrão (req, res) {if (req.method!==’GET’) {res.setHeader (‘Permitir’, [‘GET’]); res.status (405).json ({mensagem: `Método $ {req.method} não é permitido`}); } else {const products=getProducts (); res.status (200).json (produtos); }}

Nesta rota de API, estamos apenas importando o arquivo data.json e verificando o método HTTP da solicitação.

Como queremos permitir apenas solicitações GET, podemos usar uma instrução if para verifique a propriedade do método no objeto de solicitação. Para solicitações GET, podemos responder com os dados do produto no formato JSON.

Podemos acessar essa rota de API visitando http://localhost: 3000/api/products .

[categoria].js

O segundo arquivo é [categoria].js, que corresponde ao/api/products/ rota, e busca todos os produtos de uma categoria escolhida pelo usuário. Os colchetes no nome do arquivo indicam que esta é uma rota dinâmica:

import data from’./data.json’; função de exportação getProductsByCategory (categoria) {const products=data.filter ((product)=> product.category===category); devolver produtos; } exportar manipulador de função padrão (req, res) {if (req.method!==’GET’) {res.setHeader (‘Permitir’, [‘GET’]); res.status (405).json ({mensagem: `Método $ {req.method} não é permitido`}); } else {const products=getProductsByCategory (req.query.category); res.status (200).json (produtos); }}

Esta rota de API é semelhante à rota anterior, mas com uma grande mudança. Como queremos apenas produtos de uma determinada categoria, podemos usar o método de array JavaScript filter () para verificar se a categoria do produto corresponde à categoria da consulta, req.query.category ou não.

Nós pode acessar esta rota de API visitando http://localhost: 3000/api/products/xbox ou qualquer uma das outras categorias , ps5, switch, pc ou acessórios.

data.json

E, finalmente, adicionaremos data.json, que é um arquivo JSON simples que contém uma matriz de todos os produtos disponíveis e seus detalhes:

[{“id”: 1,”produto”:”Cyberpunk 2077″,”categoria”:”xbox”,”imagem”:”https://imgur.com/3CF1UhY.png”,”price”: 36,49}, {“id”: 2,”product”:”Grand Theft Auto 5″,”category”:”xbox”,”image”:”https://imgur.com/BqNWnDB.png”,”preço”: 21,99}, {“id”: 3,”produto”:”Minecraft”,”categoria”:”xbox”,”imagem”:”https://imgur.com/LXnUnd2. png”,”preço”: 4 9,99}, {“id”: 4,”produto”:”PUBG”,”categoria”:”xbox”,”imagem”:”https://imgur.com/Ondg3Jn.png”,”preço”: 5,09} , {“id”: 5,”produto”:”FIFA 21″,”categoria”:”xbox”,”imagem”:”https://imgur.com/AzT9YMP.png”,”preço”: 17,49}, {“id”: 6,”produto”:”Battlefield 5″,”categoria”:”xbox”,”imagem”:”https://imgur.com/X3MQNVs.png”,”preço”: 29,35}, {“id”: 7,”product”:”Watch Dogs 2″,”category”:”xbox”,”image”:”https://imgur.com/v3lqCEb.png”,”price”: 18,99}, {“id”: 8,”produto”:”Fortnite”,”categoria”:”ps5″,”imagem”:”https://imgur.com/3lTxDpl.png”,”preço”: 29,99}, {“id”: 9,”produto”:”Call of Duty: Black Ops”,”categoria”:”ps5″,”imagem”:”https://imgur.com/4GvUw3G.png”,”preço”: 69,99}, {“id”: 10,”produto”:”NBA2K21 Next Generation”,”categoria”:”ps5″,”imagem”:”https://imgur.com/Mxjvkws.png”,”preço”: 69,9 9}, {“id”: 11,”produto”:”Homem-Aranha Miles Morales”,”categoria”:”ps5″,”imagem”:”https://imgur.com/guV5cUF.png”,”preço”: 29,99}, {“id”: 12,”produto”:”Resident Evil Village”,”categoria”:”ps5″,”imagem”:”https://imgur.com/1CxJz8E.png”,”preço”: 59,99}, {“id”: 13,”produto”:”Assassin’s Creed Valhalla”,”categoria”:”ps5″,”imagem”:”https://imgur.com/xJD093X.png”,”preço”: 59,99}, {“id”: 14,”produto”:”Animal Crossing”,”categoria”:”switch”,”imagem”:”https://imgur.com/1SVaEBk.png”,”preço”: 59,99}, {“id”: 15,”produto”:”The Legend of Zelda”,”categoria”:”switch”,”imagem”:”https://imgur.com/IX5eunc.png”,”preço”: 59,99}, {“id”: 16,”produto”:”Stardew Valley”,”categoria”:”switch”,”imagem”:”https://imgur.com/aL3nj5t.png”,”preço”: 14.99}, {“id”: 17,”product”:”Mario Golf Super Rush”,”category”:”switch”,”image”:”https://imgur.com/CPxlyEg.png”,”price”: 59,99}, {“id”: 18,”product”:”Super Smash Bros”,”category”:”switch”,”imagem”:”https://imgur.com/ZuLatzs.png”,”preço”: 59,99}, {“id”: 19,”produto”:”Grand Theft Auto 5″,”categoria”:”pc”,”imagem”:”https://imgur.com/9LRil4N.png”,”preço”: 29,99}, {“id”: 20,”produto”:”Battlefield V”,”categoria”:”pc”,”imagem”:”https://imgur.com/T3v629h.png”,”preço”: 39,99}, {“id”: 21,”produto”:”Red Dead Redemption 2″,”categoria”:”pc”,”imagem”:”https://imgur.com/aLObdQK.png”,”preço”: 39,99}, {“id”: 22,”produto”:”Flight Simulator 2020″,”categoria”:”pc”,”imagem”:”https://imgur.com/2IeocI8.png”,”preço”: 59,99}, {“id”: 23,”produto”:”Forza Horizon 4″,”categoria”:”pc”,”imagem”:”https://imgur.com/gLQsp6N.png”,”preço”: 59,99}, {“id”: 24,”produto”:”Minecraft”,”categoria”:”pc”,”imagem”:”https://imgur.com/qm1gaGD.png”,”preço”: 29,99}, {“id”: 25,”produto”:”Rainbow Six Seige”,”categoria”:”pc”,”imagem”:”https://imgur.com/JIgzykM.png”,”preço”: 7,99}, {“id”: 26,”produto”:”Controlador Xbox”,”categoria”:”acessórios”,”imagem”:”https://imgur.com/a964vBm.png”,”preço”: 59,0}, {“id”: 27,”produto”:”Controlador Xbox”,”categoria”:”acessórios”,”imagem”:”https://imgur.com/ntrEPb1.png”,”preço”: 69,0}, {“id”: 28,”produto”:”Teclado para jogos”,”categoria”:”acessórios”,”imagem”:”https://imgur.com/VMe3WBk.png”,”preço”: 49,99}, {“id”: 29,”produto”:”Mouse para jogos”,”categoria”:”acessórios”,”imagem”:”https://imgur.com/wvpHOCm.png”,”preço”: 29,99}, {“id”: 30,”produto”:”Switch Joy-Con”,”categoria”:”acessórios”,”imagem”:”https://imgur.com/faQ0IXH.png”,”preço”: 13,99}]

É é importante observar que leremos apenas este arquivo JSON e não escreveremos ou modificaremos nenhuma informação por meio das rotas de API.

Usar um banco de dados seria mais adequado se escrever ou modificar as informações.

Esta é a aparência da estrutura de pastas após essas mudanças:

|-api | |-produtos | |-[categoria].js | |-data.json | |-index.js |-_app.js |-index.js

Agora que temos nossa API simples configurada, vamos construir mais duas páginas: a página da loja e a página da categoria, que usam nossas rotas de API.

Adicionando as páginas da loja e da categoria

A página da loja hospeda todos os produtos da loja, enquanto a página da categoria é dinâmica, exibindo apenas produtos de uma determinada categoria.

Nessas páginas, usaremos nosso componente ProductCard personalizado. Vamos começar criando um novo arquivo chamado ProductCard.jsx na pasta de componentes:

import Image from’next/image’; importar estilos de’../styles/ProductCard.module.css’; const ProductCard=({product})=> {return (

{product.product}

{product.category}

$ {product.price}

); }; exportar ProductCard padrão;

Este componente simples exibe a imagem, o nome, a categoria e o preço do produto com um botão simples Adicionar ao carrinho . Este botão não terá nenhuma funcionalidade no momento, mas isso mudará assim que integre Redux em nosso aplicativo .

Cole os estilos fornecidos abaixo em um novo arquivo chamado ProductCard.module.css dentro da pasta de estilos:

.card {display: flex; direção flexível: coluna; }.title {tamanho da fonte: 1rem; peso da fonte: 600; }.categoria {tamanho da fonte: 0.8rem; transformação de texto: maiúsculas; }.button {largura: 100%; margem superior: 0,5 rem; preenchimento: 0,75 rem 0; fundo: transparente; transformação de texto: maiúsculas; borda: 2px totalmente preto; cursor: ponteiro; }.button: hover {background: black; cor branca; }

Agora, estamos prontos para construir as páginas reais.

Construindo a página da loja com shop.jsx

Vamos começar com a página da loja. Crie um novo arquivo chamado shop.jsx na pasta de páginas:

import ProductCard de’../components/ProductCard’; importar estilos de’../styles/ShopPage.module.css’; importar {getProducts} de’./api/products/index’; const ShopPage=({produtos})=> {return (

Todos os resultados

{ products.map ((product)=> ())}

); }; exportar ShopPage padrão; exportar função assíncrona getStaticProps () {const products=await getProducts (); retornar {adereços: {produtos}}; }

Geração estática com getStaticProps ()

Para esta página, buscaremos todos os produtos e pré-renderizaremos a página no momento da construção usando um método de busca de dados Next.js, getStaticProps ().

Assim que os produtos são buscados, eles são enviados como um suporte para o componente da página, onde podemos mapear a matriz de produtos e renderizar o componente ProductCard para cada produto.

Se usar um banco de dados e espera que os dados dos produtos mudem com o tempo, usando a regeneração estática incremental recurso com a propriedade revalidate atualiza dinamicamente os dados dos produtos, garantindo que permaneçam atualizados.

Produtos pré-renderizados em TODOS OS RESULTADOS

Construindo a página da categoria com [categoria].jsx

Vamos continuar a construir a página da categoria que usa roteamento dinâmico. Crie uma nova pasta chamada categoria dentro da pasta de páginas. Agora, dentro desta pasta, crie um novo arquivo chamado [categoria].jsx:

import {useRouter} de’next/router’; importar ProductCard de’../../components/ProductCard’; importar estilos de’../../styles/ShopPage.module.css’; import {getProductsByCategory} de’../api/products/[category]’; const CategoryPage=({produtos})=> {const router=useRouter (); return (

Resultados para {router.query.category}

{products.map ( (produto)=> ())}

); }; exportar CategoryPage padrão; exportar função assíncrona getServerSideProps (ctx) {const category=ctx.query.category; produtos const=esperar getProductsByCategory (categoria); retornar {adereços: {produtos}}; }

Para visualizar esta página, visite http://localhost: 3000/category/xyz e substitua “xyz” por qualquer categoria que especificamos.

Renderização do lado do servidor com getServerSideProps ()

Na página da categoria, usaremos um método de busca de dados diferente chamado getServerSideProps (), que usa servidor-renderização no lado.

Ao contrário do método anterior de busca de dados, getServerSideProps () busca os dados no momento da solicitação e pré-renderiza a página, em vez de no momento da construção. Este método é executado em cada solicitação, o que significa que os dados do produto não ficarão desatualizados.

página de categoria do PC.

Estilizando as páginas de produto e categoria

Finalmente, aqui está a folha de estilo para as páginas de produto e categoria que acabamos de criar:

.title {font-size: 2rem; transformação de texto: maiúsculas; margem: 0 1rem 1rem; }.container {padding: 0 2rem; margem inferior: 2rem; }.cards {display: grid; colunas de modelo de grade: repetir (ajuste automático, minmax (220px, 1fr)); gap: 2,5 rem 1 rem; itens de lugar: centro; }

Gerenciamento de estado global com Redux

Embora nosso aplicativo não exija um sistema de gerenciamento de estado global por causa de sua funcionalidade limitada, se adicionarmos mais recursos e posteriormente lidarmos com mais estado, precisaremos de um sistema de gerenciamento.

Portanto, vamos integrar o Redux em nosso aplicativo Next.js com o Redux Toolkit . Redux Toolkit é a maneira moderna e recomendada de integrar Redux.

Para adicionar Redux, pare o servidor de desenvolvimento Next.js e instale Redux Toolkit com o seguinte comando:

npm install @ reduxjs/toolkit react-redux

Assim que a instalação terminar, podemos reiniciar o servidor de desenvolvimento Next.js e configurar o Redux.

Adicionando o carrinho de compras com createSlice ()

Para manter as coisas organizadas e separe a lógica do Redux do resto de nossa aplicação, crie uma nova pasta chamada redux na raiz do diretório do projeto.

Com isso, vamos realizar quatro ações:

Adicionar um item a o carrinho Aumentar a quantidade de um item no carrinho Diminuir a quantidade de um item no carrinho Remover um item do carrinho inteiramente

Vamos criar uma fatia Redux para lidar com essas ações e o redutor do carrinho. Crie um novo arquivo chamado cart.slice.js dentro da pasta redux. Não é necessário nomear o arquivo desta maneira, mas pode manter tudo organizado:

import {createSlice} from’@ reduxjs/toolkit’; const cartSlice=createSlice ({name:’cart’, initialState: [], redutores: {addToCart: (state, action)=> {const itemExists=state.find ((item)=> item.id===action. payload.id); if (itemExists) {itemExists.quantity ++;} else {state.push ({… action.payload, quantidade: 1});}}, incrementQuantity: (estado, ação)=> {item const=state.find ((item)=> item.id===action.payload); item.quantity ++;}, decrementQuantity: (state, action)=> {const item=state.find ((item)=> item.id===action.payload); if (item.quantity===1) {const index=state.findIndex ((item)=> item.id===action.payload); state.splice (index, 1);} else {item.quantity–;}}, removeFromCart: (estado, ação)=> {índice const=estado.findIndex ((item)=> item.id===action.payload); estado. splice (índice, 1);},},}); export const cartReducer=cartSlice.reducer; exportar const {addToCart, incrementQuantity, decrementQuantity, removeFromCart,}=cartSlice.actions;

O método createSlice () do Redux Toolkit aceita o nome da fatia, seu estado inicial e as funções do redutor para gerar automaticamente criadores de ação e tipos de ação que correspondem aos redutores e ao estado.

addToCart função redutora

A função redutora addToCart recebe o objeto do produto como a carga útil e verifica se um produto já existe no carrinho usando o método de array find () em JavaScript.

Se ele existir , podemos incrementar a quantidade de um produto no carrinho em 1. Caso contrário, podemos usar o método de array push () para adicioná-lo ao carrinho e definir a quantidade como 1.

função redutora incrementQuantity

incrementQuantity uma função redutora simples que recebe um ID do produto como carga útil e o usa para localizar o item no carrinho. A quantidade do item é então incrementada em 1.

função redutora decrementQuantity

decrementQuantity é semelhante à função redutora incrementQuantity, mas devemos verificar se a quantidade do produto é 1 ou não antes de decrementar.

Se for 1, podemos retirar o produto do carrinho usando o método splice (), uma vez que não pode ter quantidade 0. No entanto, se sua quantidade não for 1, podemos simplesmente diminuí-la em 1.

função do redutor removeFromCart

A função do redutor removeFromCart recebe o ID do produto como a carga útil e usa o find ( ) e splice () para remover o produto do carrinho.

Finalmente, podemos exportar o redutor de cartSlice.reducer e passá-lo para nossa loja Redux e as ações de cartSlice.actions para usar em nossos componentes.

Configurando a loja Redux

A configuração da loja Redux é um processo direto graças ao método configureStore () no Redux Toolkit. Crie um novo arquivo chamado store.js dentro da pasta redux para manter toda a lógica relacionada ao armazenamento Redux:

import {configureStore} from’@ reduxjs/toolkit’; importar {cartReducer} de’./cart.slice’; redutor const={carrinho: cartRedutor,}; const store=configureStore ({redutor,}); armazenamento padrão de exportação;

Agora, vá para o arquivo _app.js para embrulhar nosso componente com do react-redux, que leva nosso armazenamento Redux como um suporte para que todos os componentes em nosso aplicativo possam usar o estado global:

import {Provider} from’react-redux’;//Importando o provedor de importação de Navbar de’../components/Navbar’; importar rodapé de’../components/Footer’; importar loja de’../redux/store’;//Importando redux store import’../styles/globals.css’; function MyApp ({Component, pageProps}) {return (

); } exportar MyApp padrão;

E pronto, concluímos a integração do Redux em nosso aplicativo Next.js para gerenciamento de estado global.

Funcionalidade addToCart

Agora, vamos usar a ação addToCart em nosso componente ProductCard to add a product to the cart. Open the ProductCard.jsx component and import the useDispatch Hook from react-redux, as well as the addToCart action from the cart.slice.js file.

At the moment, the Add to Cart button doesn’t do anything, but we can dispatch the addToCart action when a user clicks the button. To do this, add the following to ProductCard.jsx:

import Image from’next/image’; import { useDispatch } from’react-redux’; import { addToCart } from’../redux/cart.slice’; import styles from’../styles/ProductCard.module.css’; const ProductCard=({ product })=> { const dispatch=useDispatch(); return (

{product.product}

{product.category}

$ {product.price}

); }; export default ProductCard;

Now, each time we click the button, a product adds to the cart. However, we haven’t built the cart page yet, so we cannot confirm this behavior.

If you have the Redux Devtools extension installed on your browser, you can use it to monitor state.

Building the cart page

Finally, let’s build the last page of our shopping cart application: the cart page. As always, create a new file named cart.jsx inside the pages folder.

Subsequently, create a new file named CartPage.styles.css inside the styles folder for the stylesheet.

First, import the useSelector and useDispatch Hooks from react-redux.

The useSelector Hook extracts data from the Redux store using a selector function. Then, the useDispatch Hook dispatches the action creators:

import Image from’next/image’;//Importing hooks from react-redux import { useSelector, useDispatch } from’react-redux’; import styles from’../styles/CartPage.module.css’; const CartPage=()=> { //Extracting cart state from redux store const cart=useSelector((state)=> state.cart);//Reference to the dispatch function from redux store const dispatch=useDispatch(); return (

{cart.length===0 ? (

Your Cart is Empty!

): ( <>

Image
Product
Price
Quantity
Actions
Total Price

{cart.map((item)=> (

{item.product}

$ {item.price}

{item.quantity}

$ {item.quantity * item.price}

))}

Grand Total: $ {getTotalPrice()}

)}

); }; export default CartPage;

We must also add the three buttons for incrementing, decrementing, and removing an item from the cart. So, let’s go ahead and import incrementQuantity, decrementQuantity, and removeFromCart from the cart slice:

import Image from’next/image’; import { useSelector, useDispatch } from’react-redux’;//Importing actions from cart.slice.js import { incrementQuantity, decrementQuantity, removeFromCart, } from’../redux/cart.slice’; import styles from’../styles/CartPage.module.css’; const CartPage=()=> { const cart=useSelector((state)=> state.cart); const dispatch=useDispatch(); const getTotalPrice=()=> { return cart.reduce( (accumulator, item)=> accumulator + item.quantity * item.price, 0 ); }; return (

{cart.length===0 ? (

Your Cart is Empty!

): ( <>

Image
Product
Price
Quantity
Actions
Total Price

{cart.map((item)=> (

{item.product}

$ {item.price}

{item.quantity}

$ {item.quantity * item.price}

))}

Grand Total: $ {getTotalPrice()}

)}

); }; export default CartPage;

The getTotalPrice function uses the reduce() array method to calculate the cost of all items in the cart.

For the JSX part, we check whether the cart is empty or not by accessing the cart.length property. If it is empty, we can display text notifying the user the cart is empty.

Otherwise, we can use the map() array method to render a div with all the product details. Notice that we also have three buttons with onClick to dispatch the respective cart actions when clicked.

To style the cart page, add the following to CartPage.module.css:

.container { padding: 2rem; text-align: center; }.header { margin-top: 2rem; display: flex; justify-content: space-between; }.header div { flex: 1; text-align: center; font-size: 1rem; font-weight: bold; padding-bottom: 0.5rem; text-transform: uppercase; border-bottom: 2px solid black; margin-bottom: 2rem; }.body { display: flex; justify-content: space-between; align-items: center; text-align: center; margin-bottom: 1rem; }.body > * { flex: 1; }.image { width: 100px; }.buttons > * { width: 25px; height: 30px; background-color: black; color: white; border: none; margin: 0.5rem; font-size: 1rem; }

And, we have our final cart page!

Modifying the Navbar

At the moment, there’s no feedback or notification to confirm whether a product has been added to the cart or not when clicking the Add to Cart button.

Therefore, let’s add the cart items count to the Navbar component so it increments every time we add a product. Go to Navbar.jsx and modify the code to select the cart from the global state and create a custom function to get the item count:

import Link from’next/link’; import { useSelector } from’react-redux’; import styles from’../styles/Navbar.module.css’; const Navbar=()=> { //Selecting cart from global state const cart=useSelector((state)=> state.cart);//Getting the count of items const getItemsCount=()=> { return cart.reduce((accumulator, item)=> accumulator + item.quantity, 0); }; return (

); }; export default Navbar;

Conclusion

That’s it for this project! I hope this gives you a solid understanding of the Next.js basics with Redux integration. Feel free to add more features to this project like authentication, a single product page, or payment integration.

If you face any difficulties while building this project, visit this GitHub repository to compare your code with mine.