¶Pendahuluan
Pada project kali ini, kita akan membuat beberapa component menggunkan reactjs, tujuannya agar kita dapat membuat reusable UI (User Interface) yang dapat digunakan secara fleksibel. Component juga dapat digunakan untuk memisahkan logika tampilan dan logika dari apilkasi utama, sehingga mempermudah dalam pengembangan, pemeliharaan, dan pengujian kode.
¶Installasi Package Tambahan
Dikarenakan kita akan menggunakan sweetAlert
dan package lain disini kita akan lakukan installasi terlebih dahulu, silahkan teman teman jalankan perintah berikut ini.
Terminal
npm install clsx sweetalert2 @tabler/icons-react
Pada perintah diatas kita melakukan installasi 3 buah package diantaranya sebagai berikut :
-
clsx
-
sweetAlert
-
@tabler/icons-react
¶Berkenalan dengan Props
Props merupakan sebuah element penting di react.js, kegunaanya adalah untuk mengirimkan data dari sebuah komponen ke komponen lainnya. Props sendiri dapat digunakan untuk mengirimkan data dalam bentuk string
, object
, array
, method
dan bahkan komponen
lainnya. contohnya kurang lebih seperti berikut ini :
Catatan : contoh ini tidak perlu diikuti karena kita tidak akan menggunakan component tersebut pada project kita.
Disini kita akan membuat sebuah component yang kita beri dengan nama Label.jsx
.
Label.jsx
import React from 'react'
export default function Label({text}) {
return (
<div>{text}</div>
)
}
Pada component diatas kita membuat sebuah react functional component dengan nama label
, kemudian kita masukan sebuah props dengan nama text
.
Selanjutnya ketika kita menggunakan component label
dan kita mendefiniskan sebuah props text
maka component label akan dirender dengan props yang kita kirimkan, contohnya kurang lebih seperti berikut ini :
App.jsx
import './App.css'
import Label from './components/Label'
function App() {
return (
<>
<Label text={'Belajar Props Jurnalkoding.com'}/>
</>
)
}
export default App
Maka output yang dihasilkan kurang lebih seperti gambar dibawah ini :
Setelah mengenal tentang props, disini kita akan lanjutkan untuk membuat beberapa component yang nantinya akan kita gunakan didalam project kita.
¶Component Card
Silahkan teman - teman buat file baru dengan nama Card.jsx
yang diletakan didalam folder app/resources/js/Components
, kemudian masukan kode berikut ini.
Card.jsx
import React from 'react'
export default function Card({ title, children, className }) {
return (
<>
<div className={`p-4 rounded-t-lg border ${className} bg-white`}>
<div className='flex items-center gap-2 font-semibold text-sm text-gray-700 capitalize'>
{title}
</div>
</div>
<div className='bg-white p-4 border border-t-0 border-b rounded-b-lg'>
{children}
</div>
</>
)
}
Pada kode diatas, pertama kita lakukan import React terlebih dahulu.
Card.jsx
import React from 'react'
Selanjutnya kita membuat sebuah React Functional Component dengan beberapa props diantara-nya title
, children
dan className
.
Card.jsx
export default function Card({ title, children, className })
¶Component Input
Silahkan teman - teman buat file baru dengan nama Input.jsx
yang diletakan didalam folder app/resources/js/Components
, kemudian masukan kode berikut ini :
Input.jsx
import React from 'react'
export default function Input({label, type, className, errors, ...props}) {
return (
<div className='flex flex-col gap-2'>
<label className='text-gray-600 text-sm'>
{label}
</label>
<input
type={type}
className={`w-full px-4 py-2 border text-sm rounded-md focus:outline-none focus:ring-0 bg-white text-gray-700 focus:border-gray-200 border-gray-200 ${className}`}
{...props}
/>
{errors && (
<small className='text-xs text-red-500'>{errors}</small>
)}
</div>
)
}
Pada kode diatas, pertama kita lakukan import React terlebih dahulu.
Input.jsx
import React from 'react'
Selanjutnya kita membuat sebuah React Functional Component dengan beberapa props diantara-nya label
, type
, className
, errors
dan ...props
.
Input.jsx
export default function Input({label, type, className, errors, ...props})
¶Component Checkbox
Silahkan teman - teman buka file dengan nama Checkbox.jsx
yang terletak didalam folder app/resources/js/Components
, kemudian replace dengan kode berikut ini :
Checkbox.jsx
export default function Checkbox({ label, ...props }) {
return (
<div>
<div className="flex flex-row items-center gap-2">
<input
{...props}
type="checkbox"
className={'rounded-md bg-white border-gray-200 checked:bg-teal-500'}
/>
<label className="text-sm text-gray-700">{label}</label>
</div>
</div>
);
}
Pada kode diatas, pertama kita lakukan import React terlebih dahulu.
Checkbox.jsx
import React from 'react'
Selanjutnya kita membuat sebuah React Functional Component dengan beberapa props diantara-nya label
dan ...props
.
Checkbox.jsx
export default function Checkbox({ label, ...props })
¶Component Container
Silahkan teman - teman buat file baru dengan nama Container.jsx
yang terletak didalam folder app/resources/js/Components
, kemudian masukan kode berikut ini :
Container.jsx
import React from 'react'
export default function Container({children}) {
return (
<div className="py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{children}
</div>
</div>
)
}
Pada kode diatas, pertama kita lakukan import React terlebih dahulu.
Container.jsx
import React from 'react'
Selanjutnya kita membuat sebuah React Functional Component dengan sebuah props children
.
Container.jsx
export default function Container({children})
¶Component Textarea
Silahkan teman - teman buat file baru dengan nama Textarea.jsx
yang terletak didalam folder app/resources/js/Components
, kemudian masukan kode berikut ini :
Textarea.jsx
import React from 'react'
export default function Textarea({label, className, errors,...props}) {
return (
<div className='flex flex-col gap-2'>
<label className='text-gray-600 dark:text-gray-500 text-sm'>
{label}
</label>
<textarea
className={`w-full px-4 py-2 border text-sm rounded-md focus:outline-none focus:ring-0 bg-white text-gray-700 focus:border-gray-200 border-gray-200 ${className}`}
{...props}
/>
{errors && (
<small className='text-xs text-red-500'>{errors}</small>
)}
</div>
)
}
Pada kode diatas, pertama kita lakukan import React terlebih dahulu.
Textarea.jsx
import React from 'react'
Selanjutnya kita membuat sebuah React Functional Component dengan beberapa props diantara-nya label
, className
, errors
, dan ...props
.
Textarea.jsx
export default function Textarea({label, className, errors, ...props})
¶Component Search
Silahkan teman - teman buat file baru dengan nama Search.jsx
yang terletak didalam folder app/resources/js/Components
, kemudian masukan kode berikut ini :
Search.jsx
import { useForm } from '@inertiajs/react';
import { IconSearch } from '@tabler/icons-react';
import React from 'react'
export default function Search({url, placeholder}) {
// define use form inertia
const {data, setData, get} = useForm({
search : '',
})
// define method searchData
const handleSearchData = (e) => {
e.preventDefault();
get(`${url}?search=${data.search}`)
}
return (
<form onSubmit={handleSearchData}>
<div className='relative'>
<input
type='text'
value={data.search}
onChange={e => setData('search', e.target.value)}
className='py-2 px-4 pr-11 block w-full rounded-lg text-sm border focus:outline-none focus:ring-0 focus:ring-gray-400 text-gray-700 bg-white border-gray-200 focus:border-gray-200'
placeholder={placeholder}/>
<div className='absolute inset-y-0 right-0 flex items-center pointer-events-none pr-4'>
<IconSearch size={18} strokeWidth={1.5}/>
</div>
</div>
</form>
)
}
Pada kode diatas, pertama kita import semua yang kita butuhkan.
Search.jsx
import { useForm } from '@inertiajs/react';
import { IconSearch } from '@tabler/icons-react';
import React from 'react'
Selanjutnya kita membuat sebuah React Functional Component dengan beberapa props diantara-nya label
, className
, errors
, dan ...props
.
Search.jsx
export default function Textarea({label, className, errors, ...props})
Didalam react function component, kita membuat sebuah state menggunakan form helper yang telah disediakan oleh inerita.
Search.jsx
const {data, setData, get} = useForm({
search : '',
})
Kemudian kita juga membuat sebuah method baru dengan nama handleSearchData
, method ini kita gunakan untuk melakukan pencarian data, dan method ini dijalankan ketika form di submit.
Search.jsx
// define method searchData
const handleSearchData = (e) => {
e.preventDefault();
get(`${url}?search=${data.search}`)
}
¶Component PostCard
Silahkan teman - teman buat file baru dengan nama PostCard.jsx
yang terletak didalam folder app/resources/js/Components
, kemudian masukan kode berikut ini :
PostCard.jsx
import React from 'react'
export default function PostCard({ post }) {
return (
<div className='p-8 bg-white rounded-xl border'>
<div className='font-semibold line-clamp-1'>{post.title}</div>
<div className='text-sm text-gray-500 line-clamp-2'>{post.content}</div>
<div className='text-sm font-semibold text-sky-500 mt-4'>
Posted by - <span className='underline'>{post.user.name}</span>
</div>
</div>
)
}
Pada kode diatas, pertama kita lakukan import React terlebih dahulu.
PostCard.jsx
import React from 'react'
Selanjutnya kita membuat sebuah React Functional Component dengan sebuah props post
.
PostCard.jsx
export default function PostCard({ post })
Component Pagination
Silahkan teman - teman buat file baru dengan nama Pagination.jsx
yang terletak didalam folder app/resources/js/Components
, kemudian masukan kode berikut ini :
Pagination.jsx
import React from 'react'
import { Link } from '@inertiajs/react';
import { IconChevronRight, IconChevronLeft } from '@tabler/icons-react';
export default function Pagination({ links }) {
const style = 'p-1 text-sm border rounded-md bg-white text-gray-500 hover:bg-gray-100'
return (
<>
<ul className="mt-2 lg:mt-5 justify-end flex items-center gap-1">
{links.map((item, i) => {
return item.url != null ? (
item.label.includes('Previous') ? (
<Link className={style} key={i} href={item.url}>
<IconChevronLeft size={'20'} strokeWidth={'1.5'}/>
</Link>
) : item.label.includes('Next') ? (
<Link className={style} key={i} href={item.url}>
<IconChevronRight size={'20'} strokeWidth={'1.5'}/>
</Link>
) : (
<Link className={`px-2 py-1 text-sm border rounded-md text-gray-500 hover:bg-gray-100 ${item.active ? 'bg-white text-gray-700' : 'bg-white'}`} key={i} href={item.url}>
{item.label}
</Link>
)
) : null;
})}
</ul>
</>
)
}
Pada kode diatas, pertama kita import semua yang kita butuhkan.
Pagination.jsx
import React from 'react'
import { Link } from '@inertiajs/react';
import { IconChevronRight, IconChevronLeft } from '@tabler/icons-react';
Selanjutnya kita membuat sebuah React Functional Component dengan sebuah props links
.
Pagination.jsx
export default function Pagination({ links })
¶Component Button
Silahkan teman - teman buat file baru dengan nama Button.jsx
yang terletak didalam folder app/resources/js/Components
, kemudian masukan kode berikut ini :
Button.jsx
import { Link, useForm } from '@inertiajs/react'
import { IconArrowBack, IconCheck, IconPencilCog, IconPlus, IconTrash } from '@tabler/icons-react';
import React from 'react'
import Swal from 'sweetalert2';
export default function Button({ type, url, className, children, ...props }) {
const { delete : destroy } = useForm();
const handleDeleteData = async (url) => {
Swal.fire({
title: 'Are you sure you want to delete this?',
text: 'Data is unrecoverable!',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
}).then((result) => {
if (result.isConfirmed) {
destroy(url)
Swal.fire({
title: 'Success!',
text: 'Data deleted successfully!',
icon: 'success',
showConfirmButton: false,
timer: 1500
})
}
})
}
return (
<>
{type === 'add' &&
<Link href={url} className='px-4 py-2 text-sm border rounded-lg bg-white text-gray-700 flex items-center gap-2 hover:bg-gray-100'>
<IconPlus size={18} strokeWidth={1.5}/> <span className='hidden lg:flex'>Create New Data</span>
</Link>
}
{type === 'modal' &&
<button {...props} type='button' className={`${className} px-4 py-2 text-sm border rounded-lg flex items-center gap-2`}>
{children}
</button>
}
{type === 'submit' &&
<button type='submit' className='px-4 py-2 text-sm rounded-lg border border-teal-100 bg-teal-50 text-teal-500 flex items-center gap-2 hover:bg-teal-100'>
<IconCheck size={16} strokeWidth={1.5}/> Save Data
</button>
}
{type === 'cancel' &&
<Link href={url} className='px-4 py-2 text-sm rounded-lg border border-rose-100 bg-rose-50 text-rose-500 flex items-center gap-2 hover:bg-rose-100'>
<IconArrowBack size={16} strokeWidth={1.5}/> Go Back
</Link>
}
{type === 'edit' &&
<Link href={url} className='px-4 py-2 rounded-lg bg-orange-50 text-orange-500 flex items-center gap-2 hover:bg-orange-100'>
<IconPencilCog size={16} strokeWidth={1.5}/>
</Link>
}
{type === 'delete' &&
<button onClick={() => handleDeleteData(url)} className='px-4 py-2 rounded-lg bg-rose-50 text-rose-500 flex items-center gap-2 hover:bg-rose-100'>
<IconTrash size={18} strokeWidth={1.5}/>
</button>
}
</>
)
}
Pada kode diatas, pertama kita import semua yang kita butuhkan.
Button.jsx
import { Link, useForm } from '@inertiajs/react'
import { IconArrowBack, IconCheck, IconPencilCog, IconPlus, IconTrash } from '@tabler/icons-react';
import React from 'react'
import Swal from 'sweetalert2';
Selanjutnya kita membuat sebuah React Functional Component dengan beberapa props diantara-nya type
, url
, className
, children
, dan ...props
.
Button.jsx
export default function Button({ type, url, className, children, ...props })
Didalam react function component, kita membuat sebuah state menggunakan form helper yang telah disediakan oleh inerita.
Button.jsx
const { delete : destroy } = useForm();
Kemudian kita juga membuat sebuah method baru dengan nama handleDeleteData
dan method ini kita tambahkan sebuah paramater url
yang diambil dari props url
yang kita kirimkan.
Button.jsx
const handleDeleteData = async (url) => {
Swal.fire({
title: 'Are you sure you want to delete this?',
text: 'Data is unrecoverable!',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
}).then((result) => {
if (result.isConfirmed) {
destroy(url)
Swal.fire({
title: 'Success!',
text: 'Data deleted successfully!',
icon: 'success',
showConfirmButton: false,
timer: 1500
})
}
})
}
Didalam method tersebut kita menggunakan SweetAlert
untuk menampilkan jendela konfirmasi sebelum data benar-benar dihapus.
Button.jsx
Swal.fire({
title: 'Are you sure you want to delete this?',
text: 'Data is unrecoverable!',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
})
Ketika jendela konfirmasi terbuka dan button Yes, delete it!
diklik. maka kita akan melakukan proses penghapusan data ke dalam database menggunakan form helper
yang disediakan oleh inertia.
Method handleDeleteData
akan dijalankan ketika button diklik.
Button.jsx
{type === 'delete' &&
<button onClick={() => handleDeleteData(url)} className='px-4 py-2 rounded-lg bg-rose-50 text-rose-500 flex items-center gap-2 hover:bg-rose-100'>
<IconTrash size={18} strokeWidth={1.5}/>
</button>
}
Pada component button, kita juga membuat beberapa pengkondisian sesuai dengan props type
yang dikirimkan.
Button.jsx
{type === 'add' &&
<Link href={url} className='px-4 py-2 text-sm border rounded-lg bg-white text-gray-700 flex items-center gap-2 hover:bg-gray-100'>
<IconPlus size={18} strokeWidth={1.5}/> <span className='hidden lg:flex'>Create New Data</span>
</Link>
}
{type === 'modal' &&
<button {...props} type='button' className={`${className} px-4 py-2 text-sm border rounded-lg flex items-center gap-2`}>
{children}
</button>
}
{type === 'submit' &&
<button type='submit' className='px-4 py-2 text-sm rounded-lg border border-teal-100 bg-teal-50 text-teal-500 flex items-center gap-2 hover:bg-teal-100'>
<IconCheck size={16} strokeWidth={1.5}/> Save Data
</button>
}
{type === 'cancel' &&
<Link href={url} className='px-4 py-2 text-sm rounded-lg border border-rose-100 bg-rose-50 text-rose-500 flex items-center gap-2 hover:bg-rose-100'>
<IconArrowBack size={16} strokeWidth={1.5}/> Go Back
</Link>
}
{type === 'edit' &&
<Link href={url} className='px-4 py-2 rounded-lg bg-orange-50 text-orange-500 flex items-center gap-2 hover:bg-orange-100'>
<IconPencilCog size={16} strokeWidth={1.5}/>
</Link>
}
{type === 'delete' &&
<button onClick={() => handleDeleteData(url)} className='px-4 py-2 rounded-lg bg-rose-50 text-rose-500 flex items-center gap-2 hover:bg-rose-100'>
<IconTrash size={18} strokeWidth={1.5}/>
</button>
}
¶Component Table
Silahkan teman - teman buat file baru dengan nama Table.jsx
yang terletak didalam folder app/resources/js/Components
, kemudian masukan kode berikut ini :
Table.jsx
import React from 'react'
const Card = ({ title, className, children }) => {
return (
<>
<div className={`p-4 rounded-t-lg border ${className} bg-white`}>
<div className='flex items-center gap-2 font-semibold text-sm text-gray-700 uppercase'>
{title}
</div>
</div>
<div className='bg-white rounded-b-lg border-t-0'>
{children}
</div>
</>
)
}
const Table = ({ children }) => {
return (
<div className="w-full overflow-hidden overflow-x-auto border-collapse rounded-b-lg border border-t-0">
<table className="w-full text-sm">
{children}
</table>
</div>
);
};
const Thead = ({ className, children }) => {
return (
<thead className={`${className} border-b bg-gray-50`}>{children}</thead>
);
};
const Tbody = ({ className, children }) => {
return (
<tbody className={`${className} divide-y bg-white`}>
{children}
</tbody>
);
};
const Td = ({ className, children}) => {
return (
<td
className={`${className} whitespace-nowrap p-4 align-middle text-gray-700`}
>
{children}
</td>
);
};
const Th = ({ className, children }) => {
return (
<th
scope="col"
className={`${className} h-12 px-4 text-left align-middle font-medium text-gray-700`}
>
{children}
</th>
);
};
const Empty = ({colSpan, message, children}) => {
return (
<tr>
<td colSpan={colSpan}>
<div className="flex items-center justify-center h-96">
<div className="text-center">
{children}
<div className="mt-5">
{message}
</div>
</div>
</div>
</td>
</tr>
)
}
Table.Card = Card;
Table.Thead = Thead;
Table.Tbody = Tbody;
Table.Td = Td;
Table.Th = Th;
Table.Empty = Empty;
export default Table;
Pada kode diatas, kita membuat sebuah component table menjadi beberapa sub-component yang lebih modular seperti berikut ini :
¶Subcomponent Card
Sub-component ini digunakan untuk membungkus table dengan header dan isi yang terpisah, dengan props title
, className
dan children
.
Table.jsx
const Card = ({ title, className, children }) => {
return (
<>
<div className={`p-4 rounded-t-lg border ${className} bg-white`}>
<div className='flex items-center gap-2 font-semibold text-sm text-gray-700 uppercase'>
{title}
</div>
</div>
<div className='bg-white rounded-b-lg border-t-0'>
{children}
</div>
</>
);
};
¶Subcomponent Table
Sub-component ini digunakan untuk menampilkan table dengan props children
.
Table.jsx
const Table = ({ children }) => {
return (
<div className="w-full overflow-hidden overflow-x-auto border-collapse rounded-b-lg border border-t-0">
<table className="w-full text-sm">
{children}
</table>
</div>
);
};
¶Subcomponent Thead dan Tbody
Sub-component ini digunakan untuk membungkus bagian header dan body tabel dengan props className
dan children
.
Table.jsx
const Thead = ({ className, children }) => {
return (
<thead className={`${className} border-b bg-gray-50`}>{children}</thead>
);
};
const Tbody = ({ className, children }) => {
return (
<tbody className={`${className} divide-y bg-white`}>
{children}
</tbody>
);
};
¶Subcomponent Th dan Td
Sub-component ini digunakan untuk elemen kolom header dan data dengan props className
dan children
.
Table.jsx
const Th = ({ className, children }) => {
return (
<th
scope="col"
className={`${className} h-12 px-4 text-left align-middle font-medium text-gray-700`}
>
{children}
</th>
);
};
const Td = ({ className, children }) => {
return (
<td
className={`${className} whitespace-nowrap p-4 align-middle text-gray-700`}
>
{children}
</td>
);
};
¶Subcomponent Empty
Sub-component ini digunakan untuk menampilkan pesan jika data didalam tabel kosong dengan props colspan
, message
dan children
.
Table.jsx
const Empty = ({ colSpan, message, children }) => {
return (
<tr>
<td colSpan={colSpan}>
<div className="flex items-center justify-center h-96">
<div className="text-center">
{children}
<div className="mt-5">{message}</div>
</div>
</div>
</td>
</tr>
);
};
Kemudian semua semua sub-component diatas kita jadikan sebuah properti Table
agar penggunaannya lebih rapi dengan cara kita export sebagai Table
.
Table.jsx
Table.Card = Card;
Table.Thead = Thead;
Table.Tbody = Tbody;
Table.Td = Td;
Table.Th = Th;
Table.Empty = Empty;
export default Table;
¶Penutup
Setelah berhasil membuat beberapa component yang akan kita gunakan nantinya, kita akan lanjutkan untuk pembuatan sebuah utils untuk melakukan pembatasan akses sesuai dengan roles & permissions yang dimiliki oleh user.