React Mini E-Ticaret Projesi

Aygün Bayır
5 min readSep 14, 2024

--

Merhaba arkadaşlar, bu yazımda sizlere adım adım mini bir e-ticaret projesi nasıl yapılır onu anlatmaya çalışacağım. Bu projeyi yaparken Tanstack Query, Axios ve Zustand paketlerinin kullanımına da giriş yapmış olacağız. Faydalı bir rehber olmasını umuyorum. Hadi başlayalım.

Ben VSCode uygulamasını kullanarak geliştirme yapıyorum. Siz isterseniz farklı bir editör kullanabilirsiniz.

Proje Kurulumu

Vite ile projemizi oluşturalım:

npm create vite@latest

Proje ismimizi mini-e-commerce olarak girdikten sonra React ve TypeScript seçeneklerini seçerek ilerliyoruz.

Proje dizinine terminalde girmek için:

cd mini-e-commerce

(Siz farklı isim verdiyseniz o isme ait klasöre cd komutu ile girmelisiniz.)

Projemizde gereken paketleri yüklemek için:

npm install

Projemizin klasörünü vscode penceresinde açmak için:

code .

Eğer terminalimiz kapandıysa Ctrl+J kombinasyonuna basarak terminalimizi yeniden açalım ve React projemizde gereken paketleri yüklemek için bu komutu terminale yazalım:

npm install @tanstack/react-query axios zustand

Projemize TailwindCSS ekleyeceğiz, aşağıdaki komutları teker teker terminale girin:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

tailwind.config.js dosyasının içindekileri silin ve bu kodları yapıştırın:

/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

src klasöründeki index.css içindeki kodları silin ve bunu yapıştırın:

@tailwind base;
@tailwind components;
@tailwind utilities;

TailwindCSS kurulumunu başarıyla tamamladınız, tebrik ederim.

Uygulamamızda TanStack Query paketini kullanabilmek için main.tsx dosyasında tanımlamamız ve App’i (uygulamamızı) QueryClientProvider ile sarmamız gerekiyor. Bunun için src klasöründeki main.tsx dosyasını aşağıdaki gibi düzenleyelim:

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();

createRoot(document.getElementById("root")!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</StrictMode>
);

Tanstack Query paketi artık uygulamamızda kullanılmaya hazır.

Componentlerin Oluşturulması

src klasörüne sağ tıklayıp components ismiyle yeni bir klasör oluşturalım.

components klasörü içinde Cart.tsx isminde bir dosya oluşturalım, bu bizim ürün sepeti Component’imiz olacak. Dosyamızı oluşturduktan sonra açıp içine aşağıdaki kodları yazalım:

export default function Cart() {
return (
<div>
<h2 className="text-xl font-semibold">Cart</h2>
<p>Your cart is empty.</p>
</div>
);
}

Componentimiz sadece h2 etiketiyle başlık döndürüyor. Burada kullandığımız TailwindCSS’te bulunan utility class’lar yazımıza büyüklük ve kalınlık veriyor. Aynı şekilde components klasörü içinde List.tsx isminde bir dosya daha oluşturalım ve aşağıdaki kodları yazalım:

export default function List() {
return (
<div>
<h2 className="text-xl font-semibold">Product List</h2>
</div>
);
}

Bu da tamam, artık App component’imizde yeni oluşturduğumuz componentleri import edip kullanabiliriz. src klasöründeki App.tsx içinde var olan kodları silelim ve bu kodları yazalım:

import Cart from "./components/Cart";
import List from "./components/List";

export default function App() {
return (
<div className="container mx-auto">
<h1 className="text-3xl font-bold">E-Commerce App</h1>
<Cart />
<List />
</div>
);
}

App componentimizin genel yapısı oluştu. container ve mx-auto sınıfları sayfa içeriğinin ortalanmasını ve sayfamızın responsive olmasını sağlar. Uygulamamızı terminale bu komutu girerek çalıştıralım ve sonucu tarayıcımızda http://localhost:5173 adresinde görelim:

npm run dev

Ürünleri Servisten Fetch Etmek

Product modelimizi oluşturalım. src klasörü içinde models isminde yeni bir klasör oluşturalım, daha sonra models klasörü içinde Product.ts diye bir dosya oluşturalım ve içine bu kodları yazalım:

export default interface Product {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
rate: number;
count: number;
};
quantity?: number;
}

Ürünlerimizi fakestoreapi.com API’si üzerinden çekeceğiz bunun için önce Axios dosyamızı oluşturalım. src klasörü içinde api.ts adında bir dosya oluşturun ve içine bu kodları yazın:

import axios from "axios";
import Product from "./models/Product";

const api = axios.create({
baseURL: "https://fakestoreapi.com",
});

export const getCategories = async (): Promise<string[]> => {
const response = await api.get<string[]>("/products/categories");
return response.data;
};

export const getProducts = async (): Promise<Product[]> => {
const response = await api.get<Product[]>("/products");
return response.data;
};

export const getProductById = async (id: number): Promise<Product> => {
const response = await api.get<Product>(`/products/${id}`);
return response.data;
};

Burada api adında bir axios instance (örneği) oluşturduk. Onun üzerinden fetch (veri çekme) işlemlerimizi yönettik. Ayrıca tip güvenli bir yapı oluşturduk. TypeScript kullanmanın avantajlarından faydalandık.

Zustand ile Sepet Durumunu Yönetmek

Şimdi Zustand kullanarak sepet için kullanacağımız store’u hazırlayalım, src klasörü içinde store isminde bir klasör oluşturalım ve bunun içinde useCart.ts isminde bir dosya oluşturalım, bu kodları yazalım:

import { create } from "zustand";
import Product from "../models/Product";

interface CartState {
products: Product[];
add: (product: Product) => void;
remove: (id: number) => void;
increaseQuantity: (id: number) => void;
decreaseQuantity: (id: number) => void;
}

export const useCart = create<CartState>((set) => ({
products: [],
add: (product: Product) =>
set((state) => {
const existingProduct = state.products.find(
(item) => item.id === product.id
);
if (existingProduct) {
return {
...state,
products: state.products.map((item) =>
item.id === product.id
? { ...item, quantity: (item.quantity || 0) + 1 }
: item
),
};
} else {
return {
...state,
products: [...state.products, { ...product, quantity: 1 }],
};
}
}),
remove: (id: number) =>
set((state) => ({
...state,
products: state.products.filter((product) => product.id !== id),
})),
increaseQuantity: (id: number) =>
set((state) => ({
...state,
products: state.products.map((product) =>
product.id === id
? { ...product, quantity: (product.quantity || 0) + 1 }
: product
),
})),
decreaseQuantity: (id: number) =>
set((state) => ({
...state,
products: state.products
.map((product) =>
product.id === id
? { ...product, quantity: (product.quantity || 0) - 1 }
: product
)
.filter((product) => product.quantity && product.quantity > 0),
})),
}));

Zustand ile sepetimizi oluşturduk. Ürün ekleme, silme ürün adedini arttırma ve azaltma fonksiyonlarımızı yazdık. Burada set fonksiyonu state’i değiştiren bir reducer fonksiyondur. Reducer fonksiyonlar pure fonksiyonlardır. Daha önce Redux veya Context API ile çalıştıysanız reducer kavramına aşinasınızdır. Zustand React store yönetimi için yeni ve modern bir çözüm. Dökümanını okumanızı tavsiye ederim.

Ürün Listesi ve Sepeti Tamamlamak

Artık ürünlerimizi API üzerinden fetch etmeye ve sepetimizi kullanmaya hazırız. List.tsx componentimize bu kodları yazalım:

import { useQuery } from "@tanstack/react-query";
import Product from "../models/Product";
import { getProducts } from "../api";
import { useCart } from "../store/useCart";

export default function List() {
const { isLoading, isError, data, error } = useQuery<Product[]>({
queryKey: ["products"],
queryFn: getProducts,
});

const { add } = useCart();

if (isError) {
return <div>Error: {error.message}</div>;
}

if (isLoading) {
return <div>Loading...</div>;
}

return (
<div>
<h2 className="text-xl font-semibold">Product List</h2>
<ul className="list-disc">
{data?.map((product) => (
<li key={product.id} className="mb-1">
<span>{product.title}</span>
<button
className="bg-blue-500 hover:bg-blue-700 text-white px-1 mx-1 rounded"
onClick={() => add(product)}
>
Add to cart
</button>
</li>
))}
</ul>
</div>
);
}

Burada ürünleri Tanstack Query kullanarak fetch ettik. Tanstack Query bizim için loading ve error durumlarını da yönetiyor. Eğer bir hata oluşursa ekrana hata mesajı yazılacak. Yüklenmesini beklerken de ekrana Loading… yazılacak. Ayrıca sepetimize ürün ekleme butonu da ekledik.

Sepetteki ürünleri Zustand ile oluşturduğumuz store üzerinden listelemek ve güncellemek için Cart componentimizi de değiştirmemiz gerekiyor, Cart.tsx dosyasına bu kodları yazalım:

import { useCart } from "../store/useCart";

export default function Cart() {
const { products, increaseQuantity, decreaseQuantity, remove } = useCart();

return (
<div>
<h2 className="text-xl font-semibold">Cart</h2>
{products.length === 0 && <p>Your cart is empty.</p>}
<ul className="list-disc">
{products.map((product) => (
<li key={product.id} className="mb-1">
<span>{product.title} </span>
<span>(Quantity: {product.quantity}) </span>
<button
className="bg-green-500 hover:bg-green-700 text-white px-1 mx-1 rounded"
onClick={() => increaseQuantity(product.id)}
>
Increase Qty.
</button>
<button
className="bg-red-500 hover:bg-red-700 text-white px-1 mx-1 rounded"
onClick={() => decreaseQuantity(product.id)}
>
Decrease Qty.
</button>
<button
className="bg-blue-500 hover:bg-blue-700 text-white px-1 mx-1 rounded"
onClick={() => remove(product.id)}
>
Remove
</button>
</li>
))}
</ul>
</div>
);
}

Cart sayfamızda artık sepetteki ürünler listeleniyor, ayrıca ürünlerin yanındaki butonlarla adedi arttırma, azaltma veya sepetten silme işlemlerini yapabiliyoruz.

Teşekkür

Rehberimiz burada sona eriyor, okuduğunuz için teşekkür ederim. Dilerseniz projenin kodlarına ve canlı demosuna aşağıdaki linklerden ulaşabilirsiniz.

https://github.com/aygunbyr/mini-e-commerce

https://mini-e-commerce-medium.vercel.app/

İyi çalışmalar dilerim!

--

--

Aygün Bayır
Aygün Bayır

No responses yet