Tutorial React TypeScript #4: Insert Data Dengan Rest API


Tutorial React TypeScript #4: Insert Data Dengan Rest API

Halo teman-teman semuanya, pada tutorial sebelumnya, kita telah belajar bagaimana cara menampilkan data dari REST API menggunakan React dan TypeScript. Kali ini, kita akan melanjutkan belajar dengan membuat fitur untuk menambahkan data (Insert Data) ke dalam database menggunakan API.

Dalam tutorial ini, kita akan menggunakan Axios untuk mengirim data ke backend yang dibuat dengan Laravel. Tidak hanya itu, kita juga akan menambahkan fitur upload gambar, sehingga pengguna dapat mengunggah gambar product langsung dari aplikasi React.

Langkah 1 - Insert dan Upload Data di React

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

src/views/products/create.tsx

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

// import useNavigate dari react-router
import { useNavigate } 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 ProductCreate: 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();

    // 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 storeProduct = async (e: FormEvent) => {
        e.preventDefault();
        
        // Membuat FormData
        const formData = new FormData();
        
        // Memasukkan data ke FormData
        if (image) formData.append("image", image);
        formData.append("title", title);
        formData.append("description", description);
        formData.append("price", price);
        formData.append("stock", stock);

        try {

            // Kirim data ke API
            await Api.post("/api/products", 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={storeProduct}>
                                <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"
                                        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"
                                        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"
                                                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"
                                                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 ProductCreate;

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

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

Kemudian kita import hook useNavigate dari React Router.

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

Dan kita import service API yang sudah pernah kita buat sebelumnya.

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

Selanjutnya, kita membuat interface dengan nama Errors. Interface ini akan digunakan untuk meng-handle response error dari Rest API.

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

Di atas, setiap properti bersifat opsional (?) dan berupa array string, yang bisa berisi pesan error dari server.

Dan di dalam function component ProductCreate, kita membuat beberapa state yang nanti digunakan untuk menampung data dari form.

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>({});

Di atas untuk state price dan stock, kita menggunakan tipe string karena FormData hanya mendukung string. Nantinya, sebelum dikirim ke API, nilai dari kedua state ini akan tetap dalam bentuk string agar kompatibel dengan FormData.

Untuk mempermudah dalam menggunakan hook useNavigate, maka kita masukkan ke dalam sebuah variable.

//useNavigate
const navigate = useNavigate();

Kemudian kita buat function baru dengan nama handleFileChange. Function ini yang digunakan untuk memasukkan file yang diambil dari komputer ke dalam state image.

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

Function handleFileChange di atas akan dijalankan, ketika input file gambar di dalam form dipilih.

<input
    type="file"
    onChange={handleFileChange}
    className="form-control"
/>

Setelah itu, kita buat function lagi dengan nama storeProduct. Method ini akan dijalankan ketika form disubmit.

<form onSubmit={storeProduct}>

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

	//...
	
}

Di dalam method storePost di atas, pertama kita inisialisasi FormData. Karena kita akan melakukan upload gambar, maka kita harus menggunakan FormData agar file bisa dikirim ke server.

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

Setelah berhasil di inisialisasi, langkah berikutnya adalah memasukkan data yang ada di dalam state ke dalam FormData, yaitu dengan menggunakan append.

// Memasukkan data ke FormData
if (image) formData.append("image", image);
formData.append("title", title);
formData.append("description", description);
formData.append("price", price);
formData.append("stock", stock);

Setelah data berhasil dimasukkan ke dalam FormData, langkah berikutnya adalah melakukan kirim data menggunakan Rest API dengan bantuan Axios, yaitu menggunakan method POST.

// Kirim data ke API
await Api.post("/api/products", formData);

Jika proses insert dan upload data berhasil dilakukan di dalam server, maka kita akan redirect ke dalam halaman products index.

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

Tapi jika data gagal disimpan, maka kita akan memasukkan error response validasi ke dalam state yang bernama errors.

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

Dan untuk menampilkan error di dalam JSX, kita bisa menggunakan kode seperti berikut ini.

{errors.image && (
    <div className="alert alert-danger mt-2">
        {errors.image[0]}
    </div>
)}

Langkah 2 - Konfigurasi Route Products Create

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";

// 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 />} />
        </Routes>
    );
};

export default RoutesIndex;

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

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

Kemudian kita membuat konfigurasi route dengan path products/create.

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

Langkah 3 - Uji Coba Insert dan Upload di React

Silahkan teman-teman klik button ADD NEW PRODUCT yang ada pada halaman products index atau juga bisa melalui link berikut ini http://localhost:5173/products/create, jika berhasil maka akan menampilkan halaman seperti berikut.

Silahkan klik button Save tanpa mengisi data apapun di dalam form, maka kita akan mendapatkan error validasi yang dikirimkan oleh Laravel melaluio Rest API.

Sekarang, silahkan masukkan data yang diinginkan di dalam form dan teman-teman klik Save. Jika berhasil maka kita akan diarahakan ke halaman products index dengan data yang berhasil disimpan.

Kesimpulan

Pada artikel ini, kita semua telah belajar bagaimana membuat proses insert dan upload data di React TypeScript menggunakan Rest API.

Pada artikel selanjutnya, kita semua akan belajar bagaimana cara melakukan proses edit dan update data di React TypeScript menggunakan Rest API.

Terima Kaish


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