Hej ! Når det kommer til e-handelsapplikationer, er den ene ting, vi ikke kan undgå, statsadministration med "tilføj til kurv"-funktionalitet,
i dag vil jeg vise dig, hvordan du bygger en og også en optimal måde at gøre det på uden brug af Redux, som jeg ser folk bruger meget, det kommer sammen med en masse kedelplade, som du gerne vil undgå for enhver pris.
det handler om optimering 😉 lad os tage på vejen.
Mappestruktur
inde i pages/
Hjemmeside pages/index.js
din startside skulle se nogenlunde sådan ud for første gang
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
export default function Home() {
return (
<div className={styles.container}>
<Head>
<title>Product Cart System</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<h1>this is our home page 🥺</h1>
</div>
)
}
Konfigurer produkter
Lad os nu oprette en dummy-produkter for at kunne tilføje til vores
kurv
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
const DummyProducts = [
{
id: 1,
name: 'product1',
price: 10,
image: 'https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60',
description: 'this is a dummy product description'
},
{
id: 2,
name: 'product2',
price: 20,
image: 'https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60',
description: 'this is a dummy product description'
},
{
id: 3,
name: 'product3',
price: 30,
image: 'https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60',
description: 'this is a dummy product description'
},
{
id: 4,
name: 'product4',
price: 40,
image: 'https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60',
description: 'this is a dummy product description'
},
]
...
Opvarmning
godt, vi har sat en DummyProducts-variabel med 4 objekter i et array, men vi vil ikke se noget endnu, medmindre vi kortlægger hvert objekt og viser individuelt element i 'DummyProducts'-arrayet på vores skærm
...
const DummyProducts = [...] // contains a lot of data 😮💨
export default function Home() {
return (
<div className={styles.container}>
<Head>
<title>Product Cart System</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<h1 className={styles.heading}>Cart Quantity 🛒 (0)</h1>
<div className={styles.products}>
{DummyProducts.map(product => (
<div className={styles.product} key={product.id}>
<Image src={product.image} width={200} height={200} placeholder={'blur'} blurDataURL={product.image} />
<h3 className={styles.name}>{product.name}</h3>
<p className={styles.description}>{product.description}</p>
<h4 className={styles.price}>${product.price}</h4>
<button className={styles.addToCart}>Add to cart</button>
</div>
))}
</div>
</div>
)
}
lad os se, hvad vi har.
Ja til Next.js, når som helst vi bruger en ekstern url i den indbyggede næstes billedkomponent, skal vi udtrykkeligt angive
domænet for url'en i next.config.js
fil, dette er
hvordan det gøres
inside the next.config.js file
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
domains: ['images.unsplash.com'], // <--add this
},
}
module.exports = nextConfig
sådan er det simpelthen gjort 😁 , lad os nu se, hvad vi har på vores skærm
Hurra!! 🥳
Men vent, vores side ser grim ud, lad os tilføje lidt styling til den.
inde i styles/Home.module.css
.container {
padding: 0 2rem;
}
.products{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 2rem;
position: relative;
z-index: 2;
width: 70%;
align-self: center;
margin:0 auto;
border:1px solid rgb(193, 193, 193);
padding:30px;
background-color: #fff;
margin-top:100px;
}
.heading{
font-size:2rem;
font-weight:bold;
text-align:center;
margin-bottom:30px;
padding:20px;
}
.name{
font-size:1.5rem;
font-weight:bold;
text-align:center;
}
.description{
font-size:0.9rem;
text-align:center;
}
.price{
font-size:1.2rem;
font-weight:bold;
text-align:center;
color: green;
}
.addToCart{
width:100%;
padding:14px;
background-color: #000000;
color: white;
border: none;
cursor: pointer;
}
.addToCart:hover{
background-color: rgb(69, 69, 69);
color: #ffffff;
}
Lad os se, hvad vi har
Nu er det minimalt 😎
Lad os komme til den rigtige vare
vi er færdige med visualiseringsdelen
lad os komme til den rigtige vare
Opsætning af useContext
"useContext" hook bruges til at skabe fælles data, der kan tilgås i hele komponenthierarkiet uden at overføre rekvisitterne manuelt til hvert niveau.
til næste js går vi til roden af vores applikation og sætter kontekst der, så der kan tilgås data i hele vores applikation.
inde i pages/_app.js
import '../styles/globals.css'
import React, { createContext } from 'react'
export const CartSystem = createContext()
function MyApp({ Component, pageProps }) {
return (
<CartSystem.Provider value={{}}>
<Component {...pageProps} />
</CartSystem.Provider>
)
}
export default MyApp
ligesom dette er vores useContext sat til en eksporteret variabel CartSystem
og har en klar Provider
med udefineret værdi, og det er her, vi opsætter vores reduktions- og tilstandsværdi.
Opsætning af reduktions- og tilstandsobjektet
vi skal kun bruge én tilstand, som vil være cart
med startværdien af et tomt array
import '../styles/globals.css'
import React, { createContext,useReducer } from 'react'
export const CartSystem = createContext()
const initailState = {
cart: []
}
function MyApp({ Component, pageProps }) {
const Reducers = ()=>{
}
const [state,dispatch] = useReducer(Reducers,initailState)
return (
<CartSystem.Provider value={{}}>
<Component {...pageProps} />
</CartSystem.Provider>
)
}
export default MyApp
Nu er dette en opdatering af vores pages/_app.js file
strukturen af Reducer
funktion er blevet oprettet og en initialState
objekt også.
useReducer
hook returnerer to værdier , en dispatch
og en state
Jeg vil slippe et link for at læse mere om useReducer Hook, hvis du er ny til det.
lad nu passere disse data som værdi gennem vores kontekst, så det
vores applikation kan få adgang til alle data overalt
...
return (
<CartSystem.Provider value={{state,dispatch}}>
<Component {...pageProps} />
</CartSystem.Provider>
)
...
med dette kan vi få adgang til hvad der nogensinde er i tilstanden i enhver komponent i vores applikation
Adgang til tilstandsdata fra pages/index.js
fil
for at få adgang til data fra vores produktside, skal vi bruge
useContext
krog for at få fat i data, der kommer fra CartSystem.Provider
i vores pages/_app.js
fil
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import React,{useContext} from 'react'
import {CartSystem} from './_app'
const DummyProducts = [...]
export default function Home() {
const {state,dispatch} = useContext(CartSystem)
return (
<div className={styles.container}>
<Head>
<title>Product Cart System</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<h1 className={styles.heading}>Cart Quantity 🛒 (0)</h1>
<div className={styles.products}>
{DummyProducts.map(product => (
...
javascript
vi importerer useContext
og CartSystem
fra react
og _app.js
henholdsvis.
så griber vi dataene ved hjælp af useContext hook ved at videregive
CartSystem
som et argument i useContext hook og destrukturering af værdierne state
og dispatch
fra det.
da vores oprindelige tilstand er et tomt array, vil vi ikke være i stand til at vise noget fra det, lad os prøve at oprette en funktion til at tilføje varer til indkøbskurven.
...
export default function Home() {
const {state,dispatch} = useContext(CartSystem)
const addToCart =(product)=>{
dispatch({type:'ADD_TO_CART',payload:product})
}
return (
...
vi opretter en funktion addToCart
og tildel en afsendelse med handlingstypen 'ADD_TO_CART'
og en nyttelast af den vare, der vil blive valgt , nu inde i vores reducer på pages/_app.js
fil lader oprette 'ADD_TO_CART'
handling, så vores funktion kan implementeres
...
function MyApp({ Component, pageProps }) {
const Reducers = (state,action)=>{
switch(action.type){
case 'ADD_TO_CART':
const {id, name, price,description} = action.payload
const cartItem = state.cart.find(item => item.id === id)
if (cartItem) {
return {
...state,
cart: state.cart.map(item => item.id === id ? {...item, quantity: item.quantity + 1} : item)
}
} else {
return {
...state,
cart: [...state.cart, {id, name, price, description, quantity: 1}]
}
}
default:
return state;
}
}
const [state,dispatch] = useReducer(Reducers,initailState)
...
nu tager reduceringsfunktionen to parametre state
og action
, vi bruger en switch-sætning til at kontrollere handlingen og udføre en opgavebase på handlingen, i vores tilfælde lytter vi efter ADD_TO_CART
handling.
nu hvor ADD_TO_CART
funktionen er indstillet, lad os implementere denne handling på vores Tilføj til indkøbskurv-knap og se, hvad vi har fået
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import React,{useContext,useEffect} from 'react'
import {CartSystem} from './_app'
const DummyProducts = [...]
export default function Home() {
const {state,dispatch} = useContext(CartSystem)
const addToCart =(product)=>{
dispatch({type:'ADD_TO_CART',payload:product})
}
// add all the products price in cart
const total = state.cart.reduce((total,item)=>{
return total + item.price * item.quantity
},0)
// add all product quantity items
const totalItems = state.cart.reduce((total,item)=>{
return total + item.quantity
},0)
return (
<div className={styles.container}>
<Head>
<title>Product Cart System</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<h1 className={styles.heading}>Cart Quantity 🛒 ({totalItems}) | Total Price 💰 $({total})</h1>
<div className={styles.products}>
{DummyProducts.map(product => (
<div onClick={()=>addToCart(product)} className={styles.product} key={product.id}>
<Image src={product.image} width={200} height={200} placeholder={'blur'} blurDataURL={product.image} />
<h3 className={styles.name}>{product.name}</h3>
<p className={styles.description}>{product.description}</p>
<h4 className={styles.price}>${product.price}</h4>
<button className={styles.addToCart}>Add to cart</button>
</div>
))}
</div>
</div>
)
}
Endelig
nu har vi lavet nogle ændringer i vores pages/index
fil
Jeg tilføjede to funktioner, der ville finde den samlede mængde og den samlede pris for produkterne
så tilføjede du endelig addToCart
funktion til Tilføj til kurv-knappen, og nu har vi selv et fuldt fungerende tilføje til indkøbsvogn-system
*Nu har vi bygget et minimalt tilføjelsesvognssystem uden at installere nogen biblioteker eller bruge redux, vores kode er stadig i sin minimale tilstand, det samme mål kan opnås med redux, men hvorfor stresset? *