Tutorial Next JS 14 Pemula: Belajar Component dan Manfaat Utamanya

Di era digital seperti sekarang, membangun website modern adalah langkah penting bagi perusahaan yang ingin meningkatkan bisnis mereka secara online. Sebuah website tidak hanya berfungsi sebagai wajah perusahaan di dunia digital, tetapi juga sebagai platform interaksi antara perusahaan dan pelanggannya.

Dengan website yang modern dan responsif, perusahaan dapat menjangkau lebih banyak audiens, meningkatkan kepercayaan pelanggan, dan memperkuat branding mereka. Selain itu, website yang baik juga dapat mendukung strategi pemasaran, seperti SEO dan media sosial, yang pada akhirnya membantu meningkatkan penjualan dan profit perusahaan.

Oleh karena itu, penting bagi perusahaan untuk terus belajar dan mengikuti perkembangan teknologi agar bisa memaksimalkan potensi website mereka dalam menjalankan bisnis secara online.

Mengapa Menggunakan Next.js untuk Website Modern

Salah satu framework yang banyak digunakan oleh developer untuk membangun website modern adalah Next.js. Framework ini sangat populer karena fleksibilitasnya yang memungkinkan penggunaannya baik untuk frontend maupun backend.

Dengan Next.js, perusahaan dapat membangun aplikasi web yang cepat, efisien, dan SEO-friendly, sehingga dapat meningkatkan performa website dan pengalaman pengguna. Selain itu, Next.js juga mendukung rendering server-side dan static site generation, yang membuatnya sangat cocok untuk berbagai jenis proyek web development.

Fleksibilitas inilah yang menjadikan Next.js pilihan tepat untuk perusahaan yang ingin mencoba teknologi terbaru dan menghadirkan website modern yang handal untuk bisnis mereka.

Fitur Component dalam Next.js untuk Mempercepat Development

Next.js memiliki fitur yang sangat membantu dalam mempercepat proses development, yaitu component. Component dalam Next.js memungkinkan developer untuk memecah tampilan website menjadi bagian-bagian kecil yang terpisah namun saling terhubung. Setiap component ini berfungsi secara independen, yang artinya bisa digunakan kembali di berbagai bagian lain dari aplikasi.

Dengan memanfaatkan fitur ini, proses development menjadi jauh lebih efisien karena developer tidak perlu menulis ulang kode yang sama. Selain itu, component juga mempermudah proses maintenance website.

Ketika ada perubahan yang perlu dilakukan, developer hanya perlu memperbarui component tertentu tanpa harus memodifikasi keseluruhan aplikasi. Inilah yang membuat component menjadi salah satu fitur andalan di Next.js untuk membangun website modern.

Apa Itu Component-Based Architecture dan Jenis Component dalam Next.js

Component-based architecture adalah pendekatan dalam pengembangan aplikasi web yang mengorganisir aplikasi menjadi potongan-potongan kecil yang disebut component. Setiap component memiliki fungsi spesifik dan dapat digunakan kembali di berbagai bagian aplikasi.

Pendekatan ini membuat kode lebih mudah dipahami, dipelihara, dan dikembangkan dalam jangka panjang.

Di Next.js, terdapat beberapa jenis component yang sering digunakan, antara lain:

  1. Functional Components: Ini adalah jenis component yang paling umum dalam Next.js. Component ini hanya menerima props dan mengembalikan tampilan (UI). Biasanya digunakan untuk tampilan statis atau elemen yang tidak membutuhkan banyak logika kompleks.
  2. Stateful Components: Component yang memiliki internal state, biasanya digunakan ketika aplikasi membutuhkan interaksi dinamis, seperti form input atau perubahan data secara real-time.
  3. Presentational Components: Digunakan khusus untuk merender tampilan tanpa memiliki logika bisnis yang kompleks. Presentational components fokus pada bagaimana tampilan muncul kepada pengguna.
  4. Container Components: Component yang menangani logika bisnis dan mengelola state dari aplikasi. Biasanya, container components bekerja sama dengan presentational components untuk memisahkan logika dan tampilan.

Developer perlu menggunakan component berdasarkan kebutuhan aplikasi. Misalnya, jika hanya membutuhkan tampilan sederhana, maka functional components bisa digunakan. Namun, jika ada interaksi yang lebih kompleks atau pengelolaan data, stateful dan container components menjadi pilihan yang tepat.

Dengan memanfaatkan berbagai jenis component ini, developer dapat membangun aplikasi web yang lebih terstruktur, efisien, dan mudah di-maintain.

Membuat Project Next.js Toko Sepatu Online Terbaru dengan npm

Pada tutorial ini, kita akan membahas bagaimana cara membuat project Next.js terbaru untuk toko sepatu online. Berikut adalah langkah-langkahnya beserta contoh kode yang lengkap.

Install Next.js dengan npm

Untuk memulai, pastikan npm sudah terpasang di komputer Anda. Setelah itu, buka terminal dan jalankan perintah berikut untuk membuat project Next.js baru:

npx create-next-app@latest toko-sepatu-online

Perintah ini akan membuat folder bernama toko-sepatu-online dan menginstal semua dependency yang diperlukan untuk memulai project Next.js.

Menjalankan Project Next.js

Setelah proses instalasi selesai, masuk ke direktori project yang baru saja dibuat:

cd toko-sepatu-online

Jalankan project Next.js untuk melihat apakah semuanya berjalan dengan baik:

npm run dev

Kunjungi http://localhost:3000 di browser Anda, dan Anda akan melihat halaman default Next.js.

Struktur Project

Berikut adalah struktur dasar project yang baru saja dibuat:

toko-sepatu-online/
├── node_modules/
├── public/
├── src/
│   ├── pages/
│   └── styles/
├── .gitignore
├── package.json
├── README.md
└── next.config.js

Membuat Halaman Home untuk Toko Sepatu

Buka file src/pages/index.js dan modifikasi untuk membuat halaman home bagi toko sepatu online Anda:

import Head from 'next/head';

export default function Home() {
  return (
    <div>
      <Head>
        <title>Toko Sepatu Online</title>
        <meta name="description" content="Belanja sepatu online dengan koleksi terbaru" />
      </Head>
      <header>
        <h1>Selamat Datang di Toko Sepatu Online</h1>
        <p>Koleksi sepatu terbaru untuk gaya dan kenyamanan Anda</p>
      </header>
    </div>
  );
}

Menambahkan Daftar Produk Sepatu

Untuk menampilkan daftar sepatu, buat array data produk sepatu di dalam halaman index.js. Kemudian, gunakan fungsi map untuk menampilkan daftar produk tersebut.

const products = [
  { id: 1, name: 'Sepatu Running', price: 500000 },
  { id: 2, name: 'Sepatu Basket', price: 600000 },
  { id: 3, name: 'Sepatu Casual', price: 400000 }
];

export default function Home() {
  return (
    <div>
      <Head>
        <title>Toko Sepatu Online</title>
        <meta name="description" content="Belanja sepatu online dengan koleksi terbaru" />
      </Head>
      <header>
        <h1>Selamat Datang di Toko Sepatu Online</h1>
        <p>Koleksi sepatu terbaru untuk gaya dan kenyamanan Anda</p>
      </header>
      <section>
        <h2>Produk Kami</h2>
        <ul>
          {products.map((product) => (
            <li key={product.id}>
              <h3>{product.name}</h3>
              <p>Harga: Rp {product.price}</p>
            </li>
          ))}
        </ul>
      </section>
    </div>
  );
}

Menambahkan Style pada Halaman

Untuk memberikan tampilan yang lebih baik, tambahkan CSS ke halaman Anda. Buka file src/styles/globals.css dan tambahkan gaya berikut:

body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

header {
  background-color: #f8f9fa;
  padding: 20px;
  text-align: center;
}

h1 {
  color: #333;
}

section {
  padding: 20px;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  border-bottom: 1px solid #ddd;
  padding: 10px 0;
}

Setelah itu, buka kembali halaman di browser Anda. Sekarang tampilan toko sepatu online Anda terlihat lebih rapi dan profesional.

Menambahkan Produk Baru

Untuk menambahkan produk baru, Anda hanya perlu menambahkannya ke dalam array products di file index.js:

const products = [
  { id: 1, name: 'Sepatu Running', price: 500000 },
  { id: 2, name: 'Sepatu Basket', price: 600000 },
  { id: 3, name: 'Sepatu Casual', price: 400000 },
  { id: 4, name: 'Sepatu Formal', price: 700000 }
];

Produk baru tersebut akan otomatis muncul di halaman produk tanpa perlu perubahan besar pada kode lainnya.

Membuat Beberapa Komponen Utama pada Website Sepatu Online

Dalam Next.js, kita bisa memanfaatkan fitur component untuk memecah website menjadi bagian-bagian kecil yang lebih mudah diatur dan dipelihara. Berikut adalah cara membuat beberapa komponen utama untuk website sepatu online beserta contoh koding lengkap.

Membuat Komponen Header

Komponen pertama yang akan kita buat adalah Header, yang akan menampilkan judul dan deskripsi singkat tentang toko sepatu online. Buat file Header.js di dalam folder src/components.

import React from 'react';

const Header = () => {
  return (
    <header>
      <h1>Toko Sepatu Online</h1>
      <p>Koleksi sepatu terbaru dengan harga terjangkau</p>
    </header>
  );
};

export default Header;

Setelah itu, import komponen Header ke dalam halaman utama src/pages/index.js.

import Head from 'next/head';
import Header from '../components/Header';

export default function Home() {
  return (
    <div>
      <Head>
        <title>Toko Sepatu Online</title>
        <meta name="description" content="Belanja sepatu online dengan koleksi terbaru" />
      </Head>
      <Header />
      {/* Konten lainnya */}
    </div>
  );
}

Membuat Komponen Produk

Selanjutnya, kita buat komponen Product untuk menampilkan informasi setiap produk. Buat file Product.js di dalam folder src/components.

import React from 'react';

const Product = ({ name, price }) => {
  return (
    <div>
      <h3>{name}</h3>
      <p>Harga: Rp {price}</p>
    </div>
  );
};

export default Product;

Kemudian, kita gunakan komponen Product ini di halaman utama untuk menampilkan daftar produk. Kembali ke src/pages/index.js dan tambahkan produk ke dalam halaman:

import Head from 'next/head';
import Header from '../components/Header';
import Product from '../components/Product';

export default function Home() {
  const products = [
    { id: 1, name: 'Sepatu Running', price: 500000 },
    { id: 2, name: 'Sepatu Basket', price: 600000 },
    { id: 3, name: 'Sepatu Casual', price: 400000 }
  ];

  return (
    <div>
      <Head>
        <title>Toko Sepatu Online</title>
        <meta name="description" content="Belanja sepatu online dengan koleksi terbaru" />
      </Head>
      <Header />
      <section>
        <h2>Daftar Produk</h2>
        <div>
          {products.map((product) => (
            <Product key={product.id} name={product.name} price={product.price} />
          ))}
        </div>
      </section>
    </div>
  );
}

Membuat Komponen Footer

Untuk melengkapi website, kita buat komponen Footer yang akan berisi informasi singkat tentang toko. Buat file Footer.js di dalam folder src/components.

import React from 'react';

const Footer = () => {
  return (
    <footer>
      <p>&copy; 2024 Toko Sepatu Online. All Rights Reserved.</p>
    </footer>
  );
};

export default Footer;

Sekarang, tambahkan komponen Footer ke dalam halaman utama index.js agar muncul di bagian bawah halaman.

import Head from 'next/head';
import Header from '../components/Header';
import Product from '../components/Product';
import Footer from '../components/Footer';

export default function Home() {
  const products = [
    { id: 1, name: 'Sepatu Running', price: 500000 },
    { id: 2, name: 'Sepatu Basket', price: 600000 },
    { id: 3, name: 'Sepatu Casual', price: 400000 }
  ];

  return (
    <div>
      <Head>
        <title>Toko Sepatu Online</title>
        <meta name="description" content="Belanja sepatu online dengan koleksi terbaru" />
      </Head>
      <Header />
      <section>
        <h2>Daftar Produk</h2>
        <div>
          {products.map((product) => (
            <Product key={product.id} name={product.name} price={product.price} />
          ))}
        </div>
      </section>
      <Footer />
    </div>
  );
}

Menambahkan Style untuk Komponen

Buat tampilan website lebih menarik dengan menambahkan style di file src/styles/globals.css. Berikut adalah contoh CSS yang bisa digunakan:

body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

header {
  background-color: #f8f9fa;
  padding: 20px;
  text-align: center;
}

footer {
  background-color: #343a40;
  color: #fff;
  text-align: center;
  padding: 10px 0;
  position: absolute;
  bottom: 0;
  width: 100%;
}

section {
  padding: 20px;
}

h1 {
  color: #333;
}

h3 {
  color: #007bff;
}

p {
  margin: 0;
}

Menggunakan Komponen secara Efisien

Dengan menggunakan komponen, website sepatu online Anda kini lebih terstruktur dan mudah untuk dikembangkan. Komponen Header, Product, dan Footer bisa digunakan di berbagai halaman, sehingga tidak perlu menulis ulang kode di setiap halaman. Ini tidak hanya mempercepat proses development, tetapi juga memudahkan proses maintenance di masa depan.

Komponen memungkinkan Anda untuk fokus pada setiap bagian secara terpisah dan membuat website menjadi lebih modular dan scalable.

Membuat Data Dummy pada Website Toko Sepatu dan Mengirimkan Data ke Component di Halaman Lain

Dalam membangun website dengan Next.js, salah satu kebutuhan umum adalah membuat data dummy dan mengirimkan data tersebut dari satu halaman ke halaman lainnya. Berikut adalah langkah-langkah lengkap beserta contoh koding untuk membuat data dummy dan meneruskannya antar halaman di website toko sepatu.

Membuat Data Dummy Produk Sepatu

Pertama, buat data dummy untuk produk sepatu yang bisa diakses dari berbagai halaman. Buat file baru bernama products.js di dalam folder src/data.

export const products = [
  { id: 1, name: 'Sepatu Running', price: 500000, description: 'Sepatu untuk olahraga lari.' },
  { id: 2, name: 'Sepatu Basket', price: 600000, description: 'Sepatu untuk bermain basket.' },
  { id: 3, name: 'Sepatu Casual', price: 400000, description: 'Sepatu santai untuk sehari-hari.' },
  { id: 4, name: 'Sepatu Formal', price: 700000, description: 'Sepatu formal untuk acara resmi.' }
];

Data ini akan digunakan untuk menampilkan daftar produk di halaman toko sepatu serta diteruskan ke halaman detail produk.

Menampilkan Daftar Produk di Halaman Utama

Sekarang kita tampilkan data produk di halaman utama. Buka file src/pages/index.js dan impor data dari file products.js, lalu tampilkan daftar produk menggunakan component Product.

import Head from 'next/head';
import Header from '../components/Header';
import Product from '../components/Product';
import { products } from '../data/products';
import Link from 'next/link';

export default function Home() {
  return (
    <div>
      <Head>
        <title>Toko Sepatu Online</title>
        <meta name="description" content="Belanja sepatu online dengan koleksi terbaru" />
      </Head>
      <Header />
      <section>
        <h2>Daftar Produk</h2>
        <div>
          {products.map((product) => (
            <div key={product.id}>
              <Product name={product.name} price={product.price} />
              <Link href={`/product/${product.id}`}>
                <a>Lihat Detail</a>
              </Link>
            </div>
          ))}
        </div>
      </section>
    </div>
  );
}

Pada kode di atas, setiap produk ditampilkan menggunakan component Product, dan setiap produk juga memiliki link yang mengarahkan pengguna ke halaman detail produk menggunakan Link dari Next.js.

Membuat Halaman Detail Produk

Untuk menampilkan detail produk, buat halaman dinamis menggunakan routing berbasis file di Next.js. Buat folder product di dalam src/pages, lalu buat file [id].js untuk menangani routing dinamis berdasarkan id produk.

import { products } from '../../data/products';
import { useRouter } from 'next/router';
import Head from 'next/head';

const ProductDetail = () => {
  const router = useRouter();
  const { id } = router.query;
  const product = products.find((p) => p.id === parseInt(id));

  if (!product) {
    return <p>Produk tidak ditemukan</p>;
  }

  return (
    <div>
      <Head>
        <title>{product.name} - Detail Produk</title>
        <meta name="description" content={`Detail produk untuk ${product.name}`} />
      </Head>
      <h1>{product.name}</h1>
      <p>Harga: Rp {product.price}</p>
      <p>{product.description}</p>
    </div>
  );
};

export default ProductDetail;

Pada halaman ini, id produk diambil dari URL menggunakan useRouter() dari Next.js. Produk yang sesuai dengan id tersebut kemudian dicari dari data dummy dan ditampilkan detailnya. Jika produk tidak ditemukan, akan muncul pesan "Produk tidak ditemukan".

Menghubungkan Halaman Utama dengan Halaman Detail

Kembali ke halaman utama, kita telah menambahkan link yang mengarahkan ke halaman detail produk dengan URL /product/[id]. Ketika pengguna mengklik link "Lihat Detail" pada suatu produk, mereka akan diarahkan ke halaman ProductDetail yang menampilkan informasi lengkap produk tersebut.

Misalnya, jika pengguna mengklik produk dengan id 1, mereka akan diarahkan ke URL /product/1, dan halaman detail akan menampilkan nama, harga, dan deskripsi produk tersebut.

Menambahkan Style untuk Halaman Detail

Agar tampilan lebih menarik, tambahkan style untuk halaman detail produk di file src/styles/globals.css.

h1 {
  color: #007bff;
  margin-top: 20px;
}

p {
  font-size: 18px;
}

a {
  color: #007bff;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

Mengatur Types TypeScript pada Component di Next.js

Dalam project Next.js yang menggunakan TypeScript, mengatur types pada setiap component sangat penting untuk memastikan data yang diterima atau dikirim lebih akurat dan aman. Berikut adalah langkah-langkah lengkap beserta contoh koding untuk mengatur types TypeScript pada component agar data yang diterima atau dikirim sesuai dengan yang diharapkan.

Mengaktifkan TypeScript pada Project Next.js

Jika project Next.js Anda belum menggunakan TypeScript, Anda bisa mengaktifkannya dengan menjalankan perintah berikut:

touch tsconfig.json
npm install --save-dev typescript @types/react @types/node

Setelah itu, jalankan project Next.js Anda, dan Next.js akan otomatis mengonversi file-file ke dalam format TypeScript.

Menyiapkan Data Dummy dengan TypeScript

Langkah pertama adalah membuat type untuk data dummy. Buat file baru bernama types.ts di dalam folder src untuk mendefinisikan type yang digunakan. Misalnya, kita ingin mendefinisikan type untuk produk sepatu:

export interface Product {
  id: number;
  name: string;
  price: number;
  description: string;
}

Type Product ini memiliki empat properti: id, name, price, dan description, yang semuanya wajib ada ketika produk digunakan.

Membuat Data Dummy dengan Type

Setelah mendefinisikan type, kita dapat menggunakannya dalam file products.ts yang berisi data dummy. Buka file src/data/products.ts dan gunakan type yang sudah didefinisikan:

import { Product } from '../types';

export const products: Product[] = [
  { id: 1, name: 'Sepatu Running', price: 500000, description: 'Sepatu untuk olahraga lari.' },
  { id: 2, name: 'Sepatu Basket', price: 600000, description: 'Sepatu untuk bermain basket.' },
  { id: 3, name: 'Sepatu Casual', price: 400000, description: 'Sepatu santai untuk sehari-hari.' },
  { id: 4, name: 'Sepatu Formal', price: 700000, description: 'Sepatu formal untuk acara resmi.' }
];

Di sini, kita menggunakan Product[] untuk mendefinisikan bahwa variabel products adalah array dari objek Product.

Mengatur Type pada Komponen Product

Sekarang kita akan membuat komponen Product dan menetapkan type untuk data yang diterima oleh komponen tersebut. Buka file src/components/Product.tsx dan buat komponen dengan TypeScript.

import React from 'react';
import { Product } from '../types';

interface ProductProps {
  name: string;
  price: number;
}

const ProductComponent: React.FC<ProductProps> = ({ name, price }) => {
  return (
    <div>
      <h3>{name}</h3>
      <p>Harga: Rp {price}</p>
    </div>
  );
};

export default ProductComponent;

Di sini, kita membuat interface ProductProps untuk mendefinisikan bahwa ProductComponent menerima dua props: name dan price, yang keduanya bertipe string dan number.

Menggunakan Komponen dengan Type yang Tepat

Sekarang, kita akan menggunakan ProductComponent di halaman utama. Buka file src/pages/index.tsx dan impor data serta component:

import Head from 'next/head';
import Header from '../components/Header';
import ProductComponent from '../components/Product';
import { products } from '../data/products';

export default function Home() {
  return (
    <div>
      <Head>
        <title>Toko Sepatu Online</title>
        <meta name="description" content="Belanja sepatu online dengan koleksi terbaru" />
      </Head>
      <Header />
      <section>
        <h2>Daftar Produk</h2>
        <div>
          {products.map((product) => (
            <ProductComponent key={product.id} name={product.name} price={product.price} />
          ))}
        </div>
      </section>
    </div>
  );
}

Pada bagian ini, kita memastikan bahwa setiap produk yang dilemparkan ke ProductComponent memiliki data dengan tipe yang sesuai (name: string dan price: number).

Menambahkan Type untuk Routing Dinamis

Selain mengatur types pada komponen, kita juga bisa mengatur types untuk halaman dengan routing dinamis. Buka file src/pages/product/[id].tsx dan tambahkan type untuk menangani produk:

import { useRouter } from 'next/router';
import { products } from '../../data/products';
import { Product } from '../../types';

const ProductDetail: React.FC = () => {
  const router = useRouter();
  const { id } = router.query;
  const product: Product | undefined = products.find((p) => p.id === Number(id));

  if (!product) {
    return <p>Produk tidak ditemukan</p>;
  }

  return (
    <div>
      <h1>{product.name}</h1>
      <p>Harga: Rp {product.price}</p>
      <p>{product.description}</p>
    </div>
  );
};

export default ProductDetail;

Di sini, kita memastikan bahwa data product yang ditemukan sesuai dengan type Product. Jika product tidak ditemukan, halaman akan menampilkan pesan error.

Menerapkan State Management pada Component agar Data dan Tampilan Dinamis

Dalam pengembangan aplikasi Next.js, state management sangat penting untuk membuat komponen lebih dinamis. Dengan state, kita bisa mengatur data yang berubah secara real-time, seperti interaksi pengguna atau perubahan nilai dari input. Berikut adalah langkah-langkah lengkap untuk menerapkan state management pada sebuah komponen dengan contoh koding yang rinci.

Membuat State dengan useState

Pertama, kita akan membuat komponen sederhana yang menggunakan useState untuk mengelola state di dalamnya. Misalnya, kita akan membuat komponen yang menampilkan daftar sepatu dan memungkinkan pengguna untuk menambah produk sepatu ke dalam daftar favorit.

Buat komponen baru bernama ProductList.tsx di dalam folder src/components:

import React, { useState } from 'react';

const ProductList = () => {
  const [favorites, setFavorites] = useState<string[]>([]);

  const products = ['Sepatu Running', 'Sepatu Basket', 'Sepatu Casual', 'Sepatu Formal'];

  const addFavorite = (product: string) => {
    if (!favorites.includes(product)) {
      setFavorites([...favorites, product]);
    }
  };

  return (
    <div>
      <h2>Daftar Produk</h2>
      <ul>
        {products.map((product) => (
          <li key={product}>
            {product}
            <button onClick={() => addFavorite(product)}>Tambah ke Favorit</button>
          </li>
        ))}
      </ul>

      <h3>Produk Favorit</h3>
      {favorites.length > 0 ? (
        <ul>
          {favorites.map((favorite) => (
            <li key={favorite}>{favorite}</li>
          ))}
        </ul>
      ) : (
        <p>Belum ada produk favorit</p>
      )}
    </div>
  );
};

export default ProductList;

Pada contoh di atas, kita menggunakan useState untuk menyimpan daftar produk favorit di state favorites. Komponen ini memiliki daftar produk sepatu, dan pengguna bisa menambahkan produk ke daftar favorit dengan mengklik tombol "Tambah ke Favorit". Setiap kali produk ditambahkan, state favorites diperbarui, dan daftar favorit ditampilkan secara dinamis di bawah daftar produk.

Menampilkan Komponen di Halaman Utama

Selanjutnya, kita akan menampilkan komponen ProductList di halaman utama. Buka file src/pages/index.tsx dan tambahkan ProductList ke dalamnya:

import Head from 'next/head';
import Header from '../components/Header';
import ProductList from '../components/ProductList';

export default function Home() {
  return (
    <div>
      <Head>
        <title>Toko Sepatu Online</title>
        <meta name="description" content="Belanja sepatu online dengan koleksi terbaru" />
      </Head>
      <Header />
      <ProductList />
    </div>
  );
}

Sekarang, komponen ProductList akan muncul di halaman utama. Pengguna dapat menambah produk ke daftar favorit, dan setiap produk yang dipilih akan ditampilkan secara dinamis di bagian "Produk Favorit".

Mengelola State dengan Input Pengguna

Untuk memperlihatkan lebih banyak penggunaan state, kita akan menambahkan input yang memungkinkan pengguna untuk menambahkan produk baru ke daftar produk. Kembali ke file ProductList.tsx dan tambahkan input untuk menambah produk:

import React, { useState } from 'react';

const ProductList = () => {
  const [favorites, setFavorites] = useState<string[]>([]);
  const [products, setProducts] = useState<string[]>(['Sepatu Running', 'Sepatu Basket', 'Sepatu Casual', 'Sepatu Formal']);
  const [newProduct, setNewProduct] = useState<string>('');

  const addFavorite = (product: string) => {
    if (!favorites.includes(product)) {
      setFavorites([...favorites, product]);
    }
  };

  const addProduct = () => {
    if (newProduct && !products.includes(newProduct)) {
      setProducts([...products, newProduct]);
      setNewProduct('');
    }
  };

  return (
    <div>
      <h2>Daftar Produk</h2>
      <ul>
        {products.map((product) => (
          <li key={product}>
            {product}
            <button onClick={() => addFavorite(product)}>Tambah ke Favorit</button>
          </li>
        ))}
      </ul>

      <h3>Tambah Produk Baru</h3>
      <input
        type="text"
        value={newProduct}
        onChange={(e) => setNewProduct(e.target.value)}
        placeholder="Nama produk baru"
      />
      <button onClick={addProduct}>Tambah Produk</button>

      <h3>Produk Favorit</h3>
      {favorites.length > 0 ? (
        <ul>
          {favorites.map((favorite) => (
            <li key={favorite}>{favorite}</li>
          ))}
        </ul>
      ) : (
        <p>Belum ada produk favorit</p>
      )}
    </div>
  );
};

export default ProductList;

Pada kode di atas, kita menambahkan state baru newProduct untuk menyimpan input dari pengguna. Ketika pengguna mengetikkan nama produk baru, state newProduct akan diperbarui, dan ketika tombol "Tambah Produk" diklik, produk baru akan ditambahkan ke daftar produk menggunakan setProducts. Daftar produk diperbarui secara dinamis, dan pengguna juga dapat menambahkan produk baru ke daftar favorit.

Menyimpan State di Local Storage (Opsional)

Untuk mempertahankan state meskipun halaman di-refresh, Anda bisa menggunakan local storage. Berikut adalah cara menerapkan penyimpanan data menggunakan local storage pada state produk favorit:

Tambahkan efek samping untuk menyimpan dan mengambil data dari local storage menggunakan useEffect dari React:

import React, { useState, useEffect } from 'react';

const ProductList = () => {
  const [favorites, setFavorites] = useState<string[]>([]);
  const [products, setProducts] = useState<string[]>(['Sepatu Running', 'Sepatu Basket', 'Sepatu Casual', 'Sepatu Formal']);
  const [newProduct, setNewProduct] = useState<string>('');

  useEffect(() => {
    const savedFavorites = localStorage.getItem('favorites');
    if (savedFavorites) {
      setFavorites(JSON.parse(savedFavorites));
    }
  }, []);

  useEffect(() => {
    localStorage.setItem('favorites', JSON.stringify(favorites));
  }, [favorites]);

  const addFavorite = (product: string) => {
    if (!favorites.includes(product)) {
      setFavorites([...favorites, product]);
    }
  };

  const addProduct = () => {
    if (newProduct && !products.includes(newProduct)) {
      setProducts([...products, newProduct]);
      setNewProduct('');
    }
  };

  return (
    <div>
      <h2>Daftar Produk</h2>
      <ul>
        {products.map((product) => (
          <li key={product}>
            {product}
            <button onClick={() => addFavorite(product)}>Tambah ke Favorit</button>
          </li>
        ))}
      </ul>

      <h3>Tambah Produk Baru</h3>
      <input
        type="text"
        value={newProduct}
        onChange={(e) => setNewProduct(e.target.value)}
        placeholder="Nama produk baru"
      />
      <button onClick={addProduct}>Tambah Produk</button>

      <h3>Produk Favorit</h3>
      {favorites.length > 0 ? (
        <ul>
          {favorites.map((favorite) => (
            <li key={favorite}>{favorite}</li>
          ))}
        </ul>
      ) : (
        <p>Belum ada produk favorit</p>
      )}
    </div>
  );
};

export default ProductList;

Pada contoh ini, state favorites disimpan ke dalam local storage setiap kali produk baru ditambahkan ke dalam daftar favorit, dan saat halaman dimuat kembali, data favorit diambil dari local storage.

Menerapkan Conditional Rendering pada Component di Proyek Toko Sepatu Online

Dalam sebuah aplikasi, terkadang kita perlu menampilkan elemen atau konten yang berbeda berdasarkan kondisi tertentu. Pada proyek toko sepatu online, kita bisa menggunakan conditional rendering untuk mengontrol tampilan berdasarkan kondisi seperti ketersediaan produk, status login pengguna, atau kategori produk yang dipilih.

Berikut adalah langkah-langkah untuk menerapkan conditional rendering pada sebuah component dengan contoh kode lengkap.

Membuat Komponen Produk dengan Ketersediaan Stok

Kita akan membuat komponen ProductList yang menampilkan daftar sepatu. Sepatu yang stoknya habis akan ditampilkan dengan pesan khusus, sementara sepatu yang tersedia akan menampilkan tombol "Tambah ke Keranjang". Buat file ProductList.tsx di dalam folder src/components:

import React from 'react';

interface Product {
  id: number;
  name: string;
  price: number;
  isAvailable: boolean;
}

const ProductList: React.FC = () => {
  const products: Product[] = [
    { id: 1, name: 'Sepatu Running', price: 500000, isAvailable: true },
    { id: 2, name: 'Sepatu Basket', price: 600000, isAvailable: false },
    { id: 3, name: 'Sepatu Casual', price: 400000, isAvailable: true },
    { id: 4, name: 'Sepatu Formal', price: 700000, isAvailable: false }
  ];

  return (
    <div>
      <h2>Daftar Produk</h2>
      <ul>
        {products.map((product) => (
          <li key={product.id}>
            <h3>{product.name}</h3>
            <p>Harga: Rp {product.price}</p>
            {product.isAvailable ? (
              <button>Tambah ke Keranjang</button>
            ) : (
              <p style={{ color: 'red' }}>Stok Habis</p>
            )}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ProductList;

Pada contoh di atas, kita menggunakan conditional rendering untuk menampilkan pesan "Stok Habis" jika isAvailable bernilai false. Jika isAvailable bernilai true, maka akan menampilkan tombol "Tambah ke Keranjang". Ini adalah cara dasar untuk menerapkan conditional rendering di dalam komponen berdasarkan kondisi stok produk.

Menampilkan Komponen di Halaman Utama

Untuk menampilkan komponen ini di halaman utama, buka file src/pages/index.tsx dan tambahkan komponen ProductList:

import Head from 'next/head';
import Header from '../components/Header';
import ProductList from '../components/ProductList';

export default function Home() {
  return (
    <div>
      <Head>
        <title>Toko Sepatu Online</title>
        <meta name="description" content="Belanja sepatu online dengan koleksi terbaru" />
      </Head>
      <Header />
      <ProductList />
    </div>
  );
}

Sekarang, ketika Anda membuka halaman utama, daftar produk akan ditampilkan dengan pesan khusus jika stok habis.

Conditional Rendering Berdasarkan Kategori Produk

Selanjutnya, kita akan menerapkan conditional rendering berdasarkan kategori produk yang dipilih oleh pengguna. Buat sebuah state untuk menyimpan kategori yang dipilih, dan tampilkan produk berdasarkan kategori tersebut. Modifikasi file ProductList.tsx untuk menambahkan fitur ini:

import React, { useState } from 'react';

interface Product {
  id: number;
  name: string;
  price: number;
  category: string;
  isAvailable: boolean;
}

const ProductList: React.FC = () => {
  const [selectedCategory, setSelectedCategory] = useState<string>('All');

  const products: Product[] = [
    { id: 1, name: 'Sepatu Running', price: 500000, category: 'Olahraga', isAvailable: true },
    { id: 2, name: 'Sepatu Basket', price: 600000, category: 'Olahraga', isAvailable: false },
    { id: 3, name: 'Sepatu Casual', price: 400000, category: 'Casual', isAvailable: true },
    { id: 4, name: 'Sepatu Formal', price: 700000, category: 'Formal', isAvailable: false }
  ];

  const filteredProducts = selectedCategory === 'All'
    ? products
    : products.filter((product) => product.category === selectedCategory);

  return (
    <div>
      <h2>Daftar Produk</h2>

      <div>
        <button onClick={() => setSelectedCategory('All')}>Semua</button>
        <button onClick={() => setSelectedCategory('Olahraga')}>Olahraga</button>
        <button onClick={() => setSelectedCategory('Casual')}>Casual</button>
        <button onClick={() => setSelectedCategory('Formal')}>Formal</button>
      </div>

      <ul>
        {filteredProducts.map((product) => (
          <li key={product.id}>
            <h3>{product.name}</h3>
            <p>Harga: Rp {product.price}</p>
            {product.isAvailable ? (
              <button>Tambah ke Keranjang</button>
            ) : (
              <p style={{ color: 'red' }}>Stok Habis</p>
            )}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ProductList;

Pada contoh ini, kita menambahkan tombol untuk memilih kategori produk (All, Olahraga, Casual, Formal). Berdasarkan kategori yang dipilih, daftar produk akan difilter menggunakan metode filter(). Hasil filter tersebut kemudian ditampilkan dengan conditional rendering yang sama untuk ketersediaan stok.

Menyempurnakan Tampilan dengan Conditional Rendering

Kita juga bisa menyempurnakan tampilan dengan menambahkan pesan khusus jika tidak ada produk yang sesuai dengan kategori yang dipilih. Modifikasi kode di dalam ProductList.tsx:

import React, { useState } from 'react';

interface Product {
  id: number;
  name: string;
  price: number;
  category: string;
  isAvailable: boolean;
}

const ProductList: React.FC = () => {
  const [selectedCategory, setSelectedCategory] = useState<string>('All');

  const products: Product[] = [
    { id: 1, name: 'Sepatu Running', price: 500000, category: 'Olahraga', isAvailable: true },
    { id: 2, name: 'Sepatu Basket', price: 600000, category: 'Olahraga', isAvailable: false },
    { id: 3, name: 'Sepatu Casual', price: 400000, category: 'Casual', isAvailable: true },
    { id: 4, name: 'Sepatu Formal', price: 700000, category: 'Formal', isAvailable: false }
  ];

  const filteredProducts = selectedCategory === 'All'
    ? products
    : products.filter((product) => product.category === selectedCategory);

  return (
    <div>
      <h2>Daftar Produk</h2>

      <div>
        <button onClick={() => setSelectedCategory('All')}>Semua</button>
        <button onClick={() => setSelectedCategory('Olahraga')}>Olahraga</button>
        <button onClick={() => setSelectedCategory('Casual')}>Casual</button>
        <button onClick={() => setSelectedCategory('Formal')}>Formal</button>
      </div>

      {filteredProducts.length > 0 ? (
        <ul>
          {filteredProducts.map((product) => (
            <li key={product.id}>
              <h3>{product.name}</h3>
              <p>Harga: Rp {product.price}</p>
              {product.isAvailable ? (
                <button>Tambah ke Keranjang</button>
              ) : (
                <p style={{ color: 'red' }}>Stok Habis</p>
              )}
            </li>
          ))}
        </ul>
      ) : (
        <p>Tidak ada produk untuk kategori {selectedCategory}</p>
      )}
    </div>
  );
};

export default ProductList;

Jika kategori yang dipilih tidak memiliki produk, akan muncul pesan "Tidak ada produk untuk kategori [kategori]".

Dengan menggunakan conditional rendering, Anda dapat menampilkan elemen atau konten berdasarkan kondisi tertentu. Dalam contoh ini, kita menampilkan pesan khusus untuk stok habis dan memfilter produk berdasarkan kategori yang dipilih.

Ini adalah teknik yang sangat bermanfaat untuk membuat tampilan aplikasi lebih dinamis dan interaktif, sesuai dengan kebutuhan pengguna dan data yang ada.

3 Kesalahan Utama Saat Membangun Website Menggunakan Komponen di Next.js App Router

Dalam membangun website menggunakan Next.js dan fitur app router, ada beberapa kesalahan umum yang sering dilakukan oleh pengembang. Kesalahan ini dapat menyebabkan aplikasi tidak berjalan secara efisien atau menimbulkan bug yang sulit dilacak. Berikut adalah tiga kesalahan utama beserta solusi dan contoh koding lengkap untuk menghindarinya.

1. Tidak Memisahkan Logika Bisnis dari Komponen UI

Kesalahan pertama adalah mencampuradukkan logika bisnis dengan tampilan (UI) dalam satu komponen. Ini membuat komponen menjadi sulit di-maintain dan di-reuse, serta mempersulit debugging di masa depan. Sebaiknya, pisahkan logika bisnis dari komponen UI agar komponen tetap sederhana.

Contoh kesalahan:

import React, { useState, useEffect } from 'react';

const ProductList = () => {
  const [products, setProducts] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    // Fetch data langsung di dalam komponen UI
    fetch('/api/products')
      .then((res) => res.json())
      .then((data) => {
        setProducts(data);
        setIsLoading(false);
      });
  }, []);

  return (
    <div>
      {isLoading ? <p>Loading...</p> : products.map((product) => <p key={product.id}>{product.name}</p>)}
    </div>
  );
};

export default ProductList;

Solusi: Pindahkan logika fetch data ke dalam fungsi terpisah atau gunakan custom hook.

import React, { useState, useEffect } from 'react';

// Custom hook untuk fetching data
const useProducts = () => {
  const [products, setProducts] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    fetch('/api/products')
      .then((res) => res.json())
      .then((data) => {
        setProducts(data);
        setIsLoading(false);
      });
  }, []);

  return { products, isLoading };
};

const ProductList = () => {
  const { products, isLoading } = useProducts();

  return (
    <div>
      {isLoading ? <p>Loading...</p> : products.map((product) => <p key={product.id}>{product.name}</p>)}
    </div>
  );
};

export default ProductList;

2. Tidak Menggunakan Key yang Unik saat Mapping Elemen

Kesalahan kedua adalah tidak memberikan key yang unik saat melakukan rendering list dengan map(). Ini sering menyebabkan peringatan di console, dan bisa mengakibatkan bug rendering yang tidak terduga.

Contoh kesalahan:

const ProductList = ({ products }) => {
  return (
    <ul>
      {products.map((product) => (
        <li>{product.name}</li>  // Tidak menggunakan key
      ))}
    </ul>
  );
};

Solusi: Pastikan setiap elemen dalam list memiliki key yang unik, misalnya menggunakan id dari data produk.

const ProductList = ({ products }) => {
  return (
    <ul>
      {products.map((product) => (
        <li key={product.id}>{product.name}</li>  // Menggunakan key yang unik
      ))}
    </ul>
  );
};

3. Melakukan Fetch Data di Setiap Render Tanpa Mekanisme Caching

Kesalahan ketiga adalah melakukan fetch data berulang-ulang setiap kali komponen dirender tanpa caching atau mekanisme pembatasan. Ini akan mengurangi performa aplikasi, terutama jika fetch data dilakukan dari API eksternal.

Contoh kesalahan:

const ProductList = () => {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    fetch('/api/products')  // Fetch data setiap kali komponen dirender
      .then((res) => res.json())
      .then((data) => setProducts(data));
  }, []);  // Tidak ada caching
};

Solusi: Gunakan mekanisme caching seperti useSWR untuk menghindari fetch data berulang kali.

import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

const ProductList = () => {
  const { data: products, error } = useSWR('/api/products', fetcher);

  if (error) return <p>Error loading data</p>;
  if (!products) return <p>Loading...</p>;

  return (
    <ul>
      {products.map((product) => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
};

export default ProductList;

Dengan menggunakan useSWR, data hanya akan di-fetch sekali dan disimpan di cache, sehingga performa aplikasi tetap optimal.

Penutup

Dengan memahami dan menghindari kesalahan-kesalahan umum dalam membangun website menggunakan komponen di Next.js, kita bisa menciptakan aplikasi yang lebih efisien dan mudah di-maintain. Jika Anda ingin meningkatkan kemampuan coding lebih lanjut dan belajar secara mendalam, Anda bisa belajar bersama mentor expert BuildWithAngga.

Selain mendapatkan akses selamanya, Anda juga bisa membangun portfolio berkualitas, konsultasi langsung dengan mentor, dan mendapatkan beragam benefit menarik lainnya yang akan membantu Anda menjadi web developer profesional. Mari bergabung dan raih kesuksesan di dunia web development bersama BuildWithAngga!