Daftar Isi
- Setup React Dengan Vite
- Kita Butuh Prettier
- Kita Butuh Desain
-
React Components
- Component fix/absolute data.
- Component fix/absolute data.
- Component relative data dengan props
- Component relative data dengan children
- Component relative data dengan pengkondisian props.text dan props.children
- Tailwind Arbitrary Variant untuk children svg/icon
- Menambahkan 1 property type pada component Button
- Menambahkan banyak propert dengan Spread Syntax
- Component dengan props className
- Component dengan props className default
- CLSX
- Memanggil props tidak dengan attribute
- Menambahkan type default overridingable
-
Nested Seperti Ini Salah
- Prettier printWidth, bracketSpacing, dan bracketSameLine
- Cara lain export default
- Cara lain membuat function component
- Kelebihan function component ES6
- Return JSX tidak menerima lebih dari 1 node
- Nested function components
- Memisahkan file component
- Inline destructuring props
- Membuat component memiliki banyak component
- useState
- Ekstrack ke dalam komponen
- useState dengan Object
- Manipulasi State
- Tidak lagi menggunakan class, tapi menggunakan function
- Vite
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm i
npm run dev
-
Install prettier
npm install --save-dev --save-exact prettier
- Buat file .prettierrc.json di root directory lalu buat configurasi disana
{
"tabWidth": 2,
"singleQuote": true,
"jsxSingleQuote": true,
"semi": false
}
- File .prettierrc.json berguna untuk meminimalisir conflict pada kerja tim (github) karena perbedaan format
- Daripada menjalankan command format secara terus-menerus, lebih baik gunakan fitur
format on save
. Caranya install plugin prettier pada vscode, pergi ke preferece, search format on save, lalu check (enable).
- Hapus 2 file default css dan import css pada App.jsx dan main.jsx
- Install Tailwindcss melalui framework guides Vite
npm install -D tailwindcss postcss autoprefixer
- Buat file Tailwind config dengan perintah
npx tailwindcss init -p
- Format jika ingin
- Konfigurasi path Tailwind
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
- Buat file Tailwind directive pada src
@tailwind base;
@tailwind components;
@tailwind utilities;
- import pada main.jsx
- restart npm run dev
- Coding pada App.jsx untuk test Tailwind
-
React akan lebih untung menggunakan Component
-
Menambakan class pada React menggunakan
className
-
Nama component harus berformat TitleCase
-
Permasalahan-permasalahan jika tanpa component yaitu, HTML memang mempunyai component seperti button, tapi itu hanya bawaan dan tidak memiliki style apapun. Jika suatu hari nanti kita memberikan style pada button, lalu membutuhkannya kembali, maka kita harus membuat button baru lalu memberikan style lagi (redudansi)
function Button() {
return (
<button className='bg-blue-600 text-white px-4 py-2 rounded'>
Sign In
</button>
)
}
Cara memanggilnya: <Button />
atau <Button></Button>
Muncul permasalahan: Bagaimana jika textnya ingin dijadikan relative?
- Menggunakan props
function Button(props) {
return (
<button className='bg-blue-600 text-white px-4 py-2 rounded'>
{props.text}
</button>
)
}
Cara memanggilnya: <Button text='Register' />
Muncul permasalahan: Bagaimana jika isi text ingin ditambahkan component lain?
install tabler-icons
npm install @tabler/icons --save
- Menggunakan props.children
// Component
function Button(props) {
return (
<button className='bg-blue-600 text-white px-4 py-2 rounded'>
{props.children}
</button>
)
}
// Pemanggilan
<Button>
<IconBrandTwitter />
Login
</Button>
Muncul permasalahan: Bagaimana jika ingin menggunakan kedua cara diatas secara bersamaan, yaitu props.text dan props children?
- Menggunakan Logical Operators Or ||
function Button(props) {
return (
<button className='bg-blue-600 text-white px-4 py-2 rounded'>
{props.text || props.children}
</button>
)
}
Cara memanggilnya seperti 2 component sebelumnya.
Muncul permasalahan: jika kita ingin memberikan style kepada icon, maka kita akan mengulang (redudansi) styling setiap kali menambahkan icon baru
Tailwind arbitrary variant untuk children svg/icon
[&>svg]:w-5 [&>svg]:h-5 [&>svg]:stroke-1
Muncul permasalahan: button defaultnya memiliki property type, onchange, dsb. Bagaimana cara menambahkannya ke component?
- Destructuring object props
// Component
function Button(props) {
const { children, text, type } = props
return (
<button
type={type}
className='[&>svg]:w-5 [&>svg]:h-5 [&>svg]:stroke-1 flex items-center gap-x-2 bg-blue-600 text-white px-4 py-2 rounded'
>
{text || children}
</button>
)
}
// Pemanggilan
<Button type='submit'>
<IconBrandFacebook />
Register
</Button>
<Button type='button'>
<IconBrandTwitter />
Login
</Button>
Destructuring object props:
const {children, text, type} = props;
Sama dengan:
const children = props.children;
const text = props.text;
const type = props.type;
Muncul permasalahan: bagaimana jika kita ingin menambahkan property yang lain? tidak elok jika harus ditambahkan satu-satu.
Menambahkan banyak propert dengan spread syntax
// Component
function Button(props) {
const { children, text } = props
return (
<button
{...props}
className='[&>svg]:w-5 [&>svg]:h-5 [&>svg]:stroke-1 flex items-center gap-x-2 bg-blue-600 text-white px-4 py-2 rounded'
>
{text || children}
</button>
)
}
// Pemanggilan
<Button type='submit' onClick={() => console.log('Register')}>
<IconBrandFacebook />
Register
</Button>
<Button type='button' onClick={() => console.log('Login')}>
<IconBrandTwitter />
Login
</Button>
Muncul permasalahan: bagaimana jika kita ingin menambahkan style yang berbeda pada component?
function Button(props) {
const { className, children, text } = props
return (
<button
{...props}
className={`${className} [&>svg]:w-5 [&>svg]:h-5 [&>svg]:stroke-1 flex items-center gap-x-2 text-white px-4 py-2 rounded`}
>
{text || children}
</button>
)
}
Gunakan backtick `` untuk menggunakan Template literals (Template strings)
Cara print: ${className}
Muncul permasalahan: bagaimana jika kita ingin memberikan style default?
- Gunakan default variable:
className = 'bg-blue-600'
function Button(props) {
const { className = 'bg-blue-600', children, text } = props
return (
<button
{...props}
className={`${className} [&>svg]:w-5 [&>svg]:h-5 [&>svg]:stroke-1 flex items-center gap-x-2 text-white px-4 py-2 rounded`}
>
{text || children}
</button>
)
}
clsx berguna untuk merapikan className kedalam function clsx.
npm i clsx
import clsx from 'clsx'
className={clsx(
className,
'[&>svg]:w-5 [&>svg]:h-5 [&>svg]:stroke-1 flex items-center gap-x-2 text-white px-4 py-2 rounded'
)}
<Button
{...{
type: 'button',
onClick: () => console.log('Github Clicked'),
}}
>
<IconBrandGithub />
Register
</Button>
- Pemanggilan props dengan cara ini lebih baik digunakan ketika value dan property nya sama.
Contoh:
// Anggap ini value & property nya sama
const type = 'submit'
const onClick = () => console.log('Github Clicked')
// Pemanggilan props
<Button {...{ type, onClick }}>
<IconBrandGithub />
Register
</Button>
- Tambahkan variable type beserta default value ke dalam destructuring props.
- Tambahkan property type={type} pada tag button yang ada di compenent Button
- Property type bisa dioverride pada property component Button
// .prettierrc.json
"printWidth": 80,
"bracketSpacing": true,
"bracketSameLine": true
- Jika ada lebih dari satu component pada 1 file, maka export default nya dapat dipindahkan ke akhir code.
export default App
Ketika saya melakukan ini, muncul error "Requested module does not provide export named 'default'" sehingga tampilan website kosong tanpa component. Cara memperbaikinya unik sekali, hanya dengan cara save file main.jsx. Unik bukan? padahal file main.jsx tidak disentuh.
- Cara lain untuk membuat function component yaitu dengan menggunakan style ES6. Namanya
Stateless Function Component (Arrow function)
const App = () => {
const type = 'submit'
const onClick = () => console.log('Github Clicked')
return (
<div className='bg-slate-900 grid place-content-center min-h-screen'>
<div className='flex gap-x-2'>
<Button {...{ type, onClick }}>
<IconBrandGithub />
Register
</Button>
</div>
</div>
)
}
- Kelebihan function component ES6 adalah ketika return nya hanya satu baris menjadi singkat karena tidak perlu menulis return.
const Title = () => <h1>Hello World</h1>
- Return JSX tidak menerima lebih dari 1 node. Jika ingin memaksakan lebih dari satu node, maka bisa diakali dengan node Fragment from react atau empty tag <></>
// Fragment from react
const Title = () => (
<Fragment>
<h1>Hello World</h1>
<h2>Hello Globe</h2>
</Fragment>
)
// Empty Node
const Title = () => (
<>
<h1>Hello World</h1>
<h2>Hello Globe</h2>
</>
)
- Jangan pernah gunakan nested function components walaupun ini valid, karena kata react sendiri ada bug disana (lelet)
export default function App() {
function Button(props) {
const { className = 'bg-blue-600', children, text, type = 'submit' } = props
return (
<button
{...props}
type={type}
className={clsx(
className,
'[&>svg]:w-5 [&>svg]:h-5 [&>svg]:stroke-1 flex items-center gap-x-2 text-white px-4 py-2 rounded'
)}>
{text || children}
</button>
)
}
const type = 'submit'
const onClick = () => console.log('Github Clicked')
return (
<div className='bg-slate-900 grid place-content-center min-h-screen'>
<div className='flex gap-x-2'>
<Button {...{ type, onClick }}>
<IconBrandGithub />
Register
</Button>
</div>
</div>
)
}
- Buat folder baru pada src dengan nama
Components
- Buat file baru dengan nama
Button.jsx
- Cut component Button pada file App.jsx ke file Button.jsx
- Import component Button pada file App.jsx
import Button from './Components/Button'
- Export Button pada file Button.jsx
export default Button
- Import cslx
import clsx from 'clsx'
- Deklarasi destructuring porps pada Button dapat diinlinekan (intina mah dina parameter function Button) dengan tambahan parameter
...props
export default function Button({ className = 'bg-blue-600', children, text, type = 'submit', ...props }) {
....
}
- Saat yang tepat untuk membuat component memiliki banyak component adalah seperti kasus Card
- Kurang nyaman jika harus membuat folder untuk turunan component
Cara component memiliki banyak component
- Menggunakan props pribadi
// Component Card
export default function Card({ children, title, footer }) {
return (
<div className='shadow'>
<h1 className='text-2xl font-semibold'>{title}</h1>
<div className='leading-relaxed'>{children}</div>
<div className='bg-slate-50'>{footer}</div>
</div>
)
}
// Pemanggilan Component
<Card title='Hello World' footer={<Button>Register</Button>}>
Lorem ipsum dolor sit, amet consectetur adipisicing elit.
</Card>
- Component wrap/bind
// Component
function Card({ children, title, footer }) {
return (
<div className='shadow'>
<h1 className='text-2xl font-semibold'>{title}</h1>
<div className='leading-relaxed'>{children}</div>
<div className='bg-slate-50'>{footer}</div>
</div>
)
}
function Title({ children }) {
return <h1 className='text-2xl'>{children}</h1>
}
function Footer({ children }) {
return <div className='bg-slate-50'>{children}</div>
}
Card.Title = Title
Card.Footer = Footer
export default Card
// Pemanggilan
<Card>
<Card.Title>Hello React</Card.Title>
Lorem ipsum dolor sit, amet consectetur adipisicing elit.
<Card.Footer>
<Button>Register</Button>
</Card.Footer>
</Card>
- Upgrade component wrap/bind
// Component
function Card({ children }) {
return (
<div className='shadow rounded-lg overflow-hidden bg-white'>{children}</div>
)
}
function Title({ children }) {
return (
<div className='p-4 border-b'>
<h1 className='text-xl'>{children}</h1>
</div>
)
}
function Body({ children }) {
return <div className='leading-relaxed p-4'>{children}</div>
}
function Footer({ children }) {
return <div className='bg-slate-50 p-4'>{children}</div>
}
Card.Title = Title
Card.Body = Body
Card.Footer = Footer
export default Card
// Pemanggilan
<Card>
<Card.Title>Hello React</Card.Title>
<Card.Body>
Lorem Lorem, ipsum dolor sit amet consectetur adipisicing elit.
Fugiat, dicta. Lorem ipsum dolor sit, amet consectetur adipisicing
elit. Aut, aperiam?
</Card.Body>
<Card.Footer>
<Button>Register</Button>
</Card.Footer>
</Card>
- useState hadir untuk HMR, yaitu perubahan langsung/live pada browser. Penggunaan let apalagi const tidak memengaruhi HMR.
export default function App() {
const [name, setName] = useState('Default')
function handleClick() {
setName('Eris')
}
return (
<PlaceContentCenter>
<div>{name}</div>
<Button onClick={handleClick}>Change Name</Button>
</PlaceContentCenter>
)
}
- Variable pada useState tertinggal satu langkah setelah melakukan setState. Cara mengakalinya adalaha dengan cara membuat variable baru berawalan next, seperti nextName
function handleClick() {
const nextName = 'Eris'
setName(nextName)
}
- Ketika ingin menggunakan function, jangan tambahkan () agar function tersebut tidak langsung/otomatis dipanggil di awal
- Pemanggilan setState tidak bisa dilakukan berulang dalam satu waktu, karna yang terjadi hanya akan menjalankan handle secara sekali. Untuk mengakali hal tersebut, dapat menggunakan
updater
. variable prevState dapat diganti dengan apapun.
export default function App() {
const [count, setCount] = useState(0)
function handleClick() {
// setCount(count + 1)
setCount((prevCount) => prevCount + 1)
}
return (
<PlaceContentCenter>
<div>{count}</div>
<Button onClick={handleClick}>+1</Button>
<Button
onClick={() => {
handleClick()
handleClick()
handleClick()
}}>
+3
</Button>
</PlaceContentCenter>
)
}
- Ketika memanggil function didalam arrow function, pastikan untuk menambahkan ()
- Codingan Counter pada video sebelumnya dapat dijadikan komponen terpisah. Caranya buat file baru pada folder Components sebagaimana biasanya.
- Kadangkala komponen membutuhkan variable state yang terdapat pada komponen lainnya atau ingin mengirim data. Untuk mengakali hal tersebut, state dapat ditransfer melalui props. Catatan, jika state nya hanya untuk 1 komponen, sebaiknya simpan di komponen yang bersangkutan.
- Jika props & state memiliki nama yang sama, maka bis dibongkar menjadi
<Counter {...{ count, setCount }} />
- Kalian juga dapat menambahkan initial props
- Setup
- Install tailwindcss-forms
// Install
npm install -D @tailwindcss/forms
// tailwind.config.js
module.exports = {
theme: {
// ...
},
plugins: [
require('@tailwindcss/forms'),
// ...
],
}
- label dan input dapat dijadikan komponen tersendiri agar stylenya seragam
- comment pada jsx menggunakan
/* comment */
- Membuat state untuk name dan email seperti biasa (array) akan menjadi melelahkan jika isi formnya bertambah banyak. Solusinya adalah dengan useState Object.
// useState object
const [form, setForm] = useState({
name: '',
email: '',
})
// setter object useState
function onChange(event) {
setForm({ ...form, [event.target.name]: event.target.value })
}
// menampilkan
<p>Name: {form.name || '----'}</p>
<p>Email: {form.email || '----'}</p>
- Menambahkan form onSubmit
// form
<form onSubmit={onSubmit}>...</form>
// function onSubmit
function onSubmit(event) {
event.preventDefault()
console.log(form)
}
- Membuat Component Todo
Pastikan bahwa path import komponennya sudah benar.
Path import komponen pada App.jsx tidak sama dengan import komponen pada komponen karena beda struktur folder. Sehingga jangan dicopas.