Tutorial React TypeScript #5: Edit dan Update Data Dengan Rest API


Tutorial React TypeScript #5: Edit dan Update Data Dengan Rest API

Halo teman-teman semuanya, pada tutorial sebelumnya, kita telah belajar bagaimana cara menambahkan data (insert) ke dalam database menggunakan REST API dengan React dan TypeScript. Kali ini, kita akan melanjutkan belajar dengan membuat fitur untuk mengedit dan memperbarui data yang sudah ada.

Langkah 1 - Edit dan Update Data di React

Silahkan teman-teman buat file baru dengan nama edit.tsx di dalam folder src/views/products, kemudian masukkan kode berikut ini di dalamnya.

src/views/products/edit.tsx

// import hook useState, useEffect and Interface ChangeEvent, FormEvent dari react
import { FC, useState, useEffect, ChangeEvent, FormEvent } from "react";

// import useNavigate dan useParams dari react-router
import { useNavigate, useParams } from "react-router";

// import api dari folder api
import Api from "../../api";

// Interface Errors
interface Errors {
    image?: string[];
    title?: string[];
    description?: string[];
    price?: string[];
    stock?: string[];
}

const ProductEdit: FC = () => {

    // init state untuk image, title, description, price dan stock
    const [image, setImage] = useState<File | null>(null);
    const [title, setTitle] = useState<string>("");
    const [description, setDescription] = useState<string>("");
    const [price, setPrice] = useState<string>("");
    const [stock, setStock] = useState<string>("");

    // State untuk error
    const [errors, setErrors] = useState<Errors>({});

    // init useNavigate
    const navigate = useNavigate();

    //destructuring id dari useParams
    const { id } = useParams<{ id: string }>();

    // Fetch detail product
    const fetchDetailProduct = async () => {
        try {
            // Fetch data product by id
            const response = await Api.get(`/api/products/${id}`);

            // Set data product ke state
            setTitle(response.data.data.title);
            setDescription(response.data.data.description);
            setPrice(response.data.data.price);
            setStock(response.data.data.stock);

        } catch (error: any) {
            console.error("Error fetching product:", error);
        }
    };

    // useEffect
    useEffect(() => {
        fetchDetailProduct();
    }, []);

    // Handle perubahan file
    const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
        if (e.target.files && e.target.files[0]) {
            setImage(e.target.files[0]);
        }
    };

    // Handle submit form
    const updateProduct = async (e: FormEvent) => {
        e.preventDefault();

        // Membuat FormData
        const formData = new FormData();
        if (image) formData.append("image", image);
        formData.append("title", title);
        formData.append("description", description);
        formData.append("price", price);
        formData.append("stock", stock);
        formData.append("_method", "PUT");

        try {

            // Kirim data ke API
            await Api.post(`/api/products/${id}`, formData);

            // Redirect ke halaman products
            navigate("/products");

        } catch (error: any) {

            // Set error
            setErrors(error.response.data);
        }
    };

    return (
        <div className="container mt-5">
            <div className="row">
                <div className="col-md-12">
                    <div className="card border-0 rounded-3 shadow">
                        <div className="card-body">
                            <form onSubmit={updateProduct}>
                                <div className="mb-3">
                                    <label className="form-label fw-bold">Image</label>
                                    <input
                                        type="file"
                                        onChange={handleFileChange}
                                        className="form-control"
                                    />
                                    {errors.image && (
                                        <div className="alert alert-danger mt-2">
                                            {errors.image[0]}
                                        </div>
                                    )}
                                </div>

                                <div className="mb-3">
                                    <label className="form-label fw-bold">Title</label>
                                    <input
                                        type="text"
                                        className="form-control"
                                        value={title}
                                        onChange={(e) => setTitle(e.target.value)}
                                        placeholder="Title Product"
                                    />
                                    {errors.title && (
                                        <div className="alert alert-danger mt-2">
                                            {errors.title[0]}
                                        </div>
                                    )}
                                </div>

                                <div className="mb-3">
                                    <label className="form-label fw-bold">Description</label>
                                    <textarea
                                        className="form-control"
                                        value={description}
                                        onChange={(e) => setDescription(e.target.value)}
                                        rows={5}
                                        placeholder="Description Product"
                                    />
                                    {errors.description && (
                                        <div className="alert alert-danger mt-2">
                                            {errors.description[0]}
                                        </div>
                                    )}
                                </div>

                                <div className="row">
                                    <div className="col-md-6">
                                        <div className="mb-3">
                                            <label className="form-label fw-bold">Price</label>
                                            <input
                                                type="number"
                                                className="form-control"
                                                value={price}
                                                onChange={(e) => setPrice(e.target.value)}
                                                placeholder="Price Product"
                                            />
                                        </div>
                                        {errors.price && (
                                            <div className="alert alert-danger mt-2">
                                                {errors.price[0]}
                                            </div>
                                        )}
                                    </div>
                                    <div className="col-md-6">
                                        <div className="mb-3">
                                            <label className="form-label fw-bold">Stock</label>
                                            <input
                                                type="number"
                                                className="form-control"
                                                value={stock}
                                                onChange={(e) => setStock(e.target.value)}
                                                placeholder="Stock Product"
                                            />
                                        </div>
                                        {errors.stock && (
                                            <div className="alert alert-danger mt-2">
                                                {errors.stock[0]}
                                            </div>
                                        )}
                                    </div>
                                </div>

                                <button
                                    type="submit"
                                    className="btn btn-md btn-primary rounded-5 shadow border-0"
                                >
                                    Save
                                </button>
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};

export default ProductEdit;

Dari penambahan kode di atas, pertama kita import Type FC, hook useState dan useEffect, Interface changeEvent dan FormEvent dari React.

// import hook useState, useEffect and Interface ChangeEvent, FormEvent dari react
import { FC, useState, useEffect, ChangeEvent, FormEvent } from "react";

Kemudian kita import hook useNavigate dan useParams dari React Router.

// import useNavigate dan useParams dari react-router
import { useNavigate, useParams } from "react-router";

Selanjutnya, kita import service API.

// import api dari folder api
import Api from "../../api";

Dan kita membuat interface dengan nama Errors.

// Interface Errors
interface Errors {
    image?: string[];
    title?: string[];
    description?: string[];
    price?: string[];
    stock?: string[];
}

Di dalam function component ProductEdit, kita membuat beberapa state.

// init state untuk image, title, description, price dan stock
const [image, setImage] = useState<File | null>(null);
const [title, setTitle] = useState<string>("");
const [description, setDescription] = useState<string>("");
const [price, setPrice] = useState<string>("");
const [stock, setStock] = useState<string>("");

// State untuk error
const [errors, setErrors] = useState<Errors>({});

Selanjutnya, kita inisialisasi navigate.

// init useNavigate
const navigate = useNavigate();

Kemudian kita melakukan desctruct id dari hook useParams.

//destructuring id dari useParams
const { id } = useParams<{ id: string }>();

Setelah itu, kita buat function baru dengan nama fetchDetailPost.

//method fetchDetailPost
const fetchDetailPost = async () => {

	//...
	
}

Di dalamnya kita melakukan fetching ke Rest API berdasarkan parameter id yang didapatkan dari URL.

// Fetch data product by id
const response = await Api.get(`/api/products/${id}`);

Jika data ditemukan, maka kita akan assign response data-nya ke dalam state.

// Set data product ke state
setTitle(response.data.data.title);
setDescription(response.data.data.description);
setPrice(response.data.data.price);
setStock(response.data.data.stock);

Agar function fetchDetailPost bisa jalankan saat halaman diakses, maka kita perlu memanggilnya di dalam hook useEffect.

// useEffect
useEffect(() => {
    fetchDetailProduct();
}, []);

Kita buat function lagi untuk menghandle file yang akan diupload.

// Handle perubahan file
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files[0]) {
        setImage(e.target.files[0]);
    }
};

Terakhir, kita buat function yang bernama updateProduct. Function ini akan dijalakan ketika form disumbit.

<form onSubmit={updateProduct}>

	//...
	
</form>
// Handle submit form
const updateProduct = async (e: FormEvent) => {

	//...
	
}

Di dalam function di atas, sama seperti yang ada pada function storePost pada pembahasan artikel sebelumnya, bedanya disini kita tambahkan sebuah key _method dengan value PUT pada append. Karena data yang dikirimkan adalah proses update data.

// Membuat FormData
const formData = new FormData();

if (image) formData.append("image", image);
formData.append("title", title);
formData.append("description", description);
formData.append("price", price);
formData.append("stock", stock);
formData.append("_method", "PUT");

Setelah itu, kita tinggal kirimkan data-nya melalui Axios dengan method POST.

// Kirim data ke API
await Api.post(`/api/products/${id}`, formData);

Jika proses update data berhasil dilakukan, maka kita redirect atau arahkan pada halaman products index.

// Redirect ke halaman products
navigate("/products");

Jika data gagal diupdate, maka kita assign error response validasinya ke dalam state errors.

// Set error
setErrors(error.response.data);

Jangan lupa pada input di dalam form kita berikan value yang berisi state title, description, price dan stock. Ini bertujuan agar menampilkan data yang akan diedit.

<input
  type="text"
  className="form-control"
  value={title} // tambahkan ini
  onChange={(e) => setTitle(e.target.value)}
  placeholder="Title Product"
/>

Langkah 2 - Konfigurasi Route Products Edit

Silahkan teman-teman buka file src/routes/index.tsx, kemudian ubah kode-nya menjadi seperti berikut ini.

src/routes/index.tsx

// Import FC from React
import { FC } from "react";

// Import React Router
import { Routes, Route } from "react-router";

// Import view HomePage
import Home from "../views/home";

// Import view ProductIndex
import ProductIndex from "../views/products/index";

// Import view ProductCreate
import ProductCreate from "../views/products/create";

// Import view ProductEdit
import ProductEdit from "../views/products/edit";

// Definisikan component dengan Type FC (Functional Component)
const RoutesIndex: FC = () => {
    return (
        <Routes>
            {/* Route untuk halaman utama */}
            <Route path="/" element={<Home />} />

            {/* Route untuk halaman products */}
            <Route path="/products" element={<ProductIndex />} />

            {/* Route untuk halaman create product */}
            <Route path="/products/create" element={<ProductCreate />} />

            {/* Route untuk halaman edit product */}
            <Route path="/products/edit/:id" element={<ProductEdit />} />
        </Routes>
    );
};

export default RoutesIndex;

Dari perubahan kode di atas, kita import view products edit.

// Import view ProductEdit
import ProductEdit from "../views/products/edit";

Kemudian kita buat konfigurasi route-nya dengan path /products/edit/:id.

{/* Route untuk halaman edit product */}
<Route path="/products/edit/:id" element={<ProductEdit />} />

Langkah 3 - Uji Coba Edit dan Update Data

Silahkan teman-teman klik button EDIT yang ada pada data yang dimiliki, jika berhasil maka akan menampilkan halaman form edit data seperti berikut.

Silahkan sesuaikan isinya, kemudian klik button Update. Jika berhasil maka kita akan diarahkan ke halaman products index dengan data yang telah diperbarui.

Kesimpulan

Pada artikel ini, kita telah belajar bagaimana cara menampilkan detail data product yang akan diedit pada sebuah form, kemudian melakukan proses update.

Pada artikel selanjutnya, kita semua akan belajar bagaimana cara membuat fitur proses delete data product dengan Rest API di React TypeScript.

Terima Kasih


Fika Ridaul Maulayya
Full-Stack Developer, Content Creator and CO-Founder SantriKoding.com

Suka dengan tulisan di SantriKoding? Kamu bisa memberikan dukungan dengan berdonasi atau bagikan konten ini di sosial media. Terima kasih atas dukungan Anda!

KEBIJAKAN KOMENTAR

Saat memberikan komenatar silahkan memberikan informasi lengkap tentang error, seperti: screenshot, link kode, dll. Baca aturan komentar kami