Selamat datang di post perdana saya tentang Reactjs 😄 Kali ini saya coba bahas salah satu pattern di Reactjs yang paling banyak digunakan yaituCompound Components
.
Compound dalam bahasa Indonesia artinya menggabungkan. JadiCompound Components
merupakan menggabungkan berapa komponen menjadi satu komponen.
Lhooo kan emang seperti itu kalau komponen di Reactjs 🤔
Nahh bedanya, kalau Compound Component ini komponen nya hanya bisa digunakan dalam scope tertentu saja. Kita contohkan pada HTML biasa. Di HTML terdapat tag<table />
,<tbody />
, dan<thead />
. Tag<tbody />
dan<thead />
ini adalah bagian dari<table />
dan tidak bisa di gunakan diluar<table />
(bisa sih, tapi tidak ada efeknya).
Di komponen Reactjs juga bisa dibuat seperti itu lhoo 😁 Sekarang kita coba bikin studi kasus mengenai komponenModal.
Pertama kita rancang dulu bagian-bagian pada Modal, yaitu:
- Wrapper
- Body
- Footer
Ada 3 bagian utama pada Modal, maka bisa kita buat komponen setiap bagiannya dengan nama:
<Modal />
, untuk Wrapper<Modal.Body />
, untuk Body<Modal.Footer />
, untuk Footer
*FYI: Bentuk komponen diatas disebutNamespace Component
Perancangan kita selesai, kini waktunya programming. Pertama-tama saya akan menggunakan Vite + React, kalau kalian pake create-react-app juga tidak masalah dan saya juga menggunakan UI framework yang bernamaMaterial UI.
*Note: kamu tidak perlu berpatok dengan apa yang saya gunakan, kamu bisa menggunakan CRA dengan React-bootstrap serta NPM
Pertama kita inisialisasi proyeknya terlebih dahulu menggunakan vite:
yarn create vite modal-compound--template react
Setelah inisialiasi kita buka foldernya dan install dependenciesnya:
cdmodal-compound&& yarninstall
Jika sudah di install jalankan dev servernya:
yarn dev
Install dependencies yang dibutuhkan:
yarn add @mui/material @emotion/react @emotion/styled react-nanny
react-nanny
? apaan tuh? itu merupakan utilitas tambahan untuk mencari child dari react children. Mirip seperti slot pada Vue
Kalau sudah di install kini inisialisasiApp.jsx
danmain.jsx
terlebih dahulu:App.jsx
import{Button}from"@mui/material";functionApp(){return(<div><Buttonvariant="contained">Open Modal</Button></div>);}exportdefaultApp;
main.jsx
importReactfrom"react";importReactDOMfrom"react-dom/client";importAppfrom"./App";ReactDOM.createRoot(document.getElementById("root")).render(<React.StrictMode><App/></React.StrictMode>);
Inisialisasi sudah selesai kini kita bermain di komponen modalnya. Coba membuat file di lokasisrc/components/modal/index.jsx
yang berisi:
constModalBody=()=>{}constModalFooter=()=>{}constModal=()=>{}exportdefaultModal
Setiap komponen sudah dibuat waktunya menambahkan bentuk Namespace menjadi:
constModalBody=()=>{}constModalFooter=()=>{}constModal=()=>{}Modal.Body=ModalBodyModal.Footer=ModalFooterexportdefaultModal
Sekarang kita menambahkan propchildren
disetiap bagian modal nya. Menjadi:
importReactDOMfrom"react-dom";constModalBody=({children=null})=>{return<main>{children}</main>;};constModalFooter=({children=null})=>{return<footer>{children}</footer>;};constModal=({children=null,open=false})=>{if(!open)returnnull;returnReactDOM.createPortal(<div>{children}</div>,document.getElementById("root"));};Modal.Body=ModalBody;Modal.Footer=ModalFooter;exportdefaultModal;
Pada komponen<Modal />
diatas saya menggunakan react-portal agar bisa di render di element dengan idroot
Sekarang kita coba styling simple aja untuk komponen<Modal />
ini:
import{Box,Typography}from"@mui/material";importReactDOMfrom"react-dom";constModalBody=({children=null})=>{return<main>{children}</main>;};constModalFooter=({children=null})=>{return<footer>{children}</footer>;};constModal=({children=null,open=false,title="",onClose=()=>{},})=>{if(!open)returnnull;returnReactDOM.createPortal(<><Boxposition="fixed"zIndex={20}top="50%"left="50%"sx={{transform:"translate(-50%, -50%)"}}boxShadow="rgba(149, 157, 165, 0.2) 0px 8px 24px;"bgcolor="white"p="1rem"borderRadius=".5rem"width="500px"><Boxdisplay="flex"alignItems="center"justifyContent="space-between"><Typographyvariant="h1"fontSize="1.5rem"fontWeight="bold">{title}</Typography><Typographyvariant="caption"onClick={onClose}> close</Typography></Box></Box><Boxposition="fixed"zIndex={10}bgcolor="rgba(0, 0, 0, 0.5)"width="100%"height="100%"top={0}left={0}/></>,document.getElementById("root"));};Modal.Body=ModalBody;Modal.Footer=ModalFooter;exportdefaultModal;
Kini komponen<Modal />
akan menerima propsonClose
dantitle
. Kita lanjut ke komponenApp.jsx
nya:
import{Button}from"@mui/material";import{useState}from"react";importModalfrom"./components/modal";functionApp(){const[isOpen,setIsOpen]=useState(false);consttoggle=()=>setIsOpen((isOpen)=>!isOpen);return(<div><Modalopen={isOpen}title="Simple Modal"onClose={toggle}/><Buttonvariant="contained"onClick={toggle}> Open Modal</Button></div>);}exportdefaultApp;
Hasilnya seperti ini:
Waktunya penerapan Compound Component 😄 kini saya akan menggunakan utilitasreact-nanny
untuk mencari komponen didalam children
import{Box,Typography}from"@mui/material";importReactDOMfrom"react-dom";import{getChildByType}from"react-nanny";constModalBody=({children=null})=>{return<main>{children}</main>;};constModalFooter=({children=null})=>{return<footer>{children}</footer>;};constModal=({children=null,open=false,title="",onClose=()=>{},})=>{constbody=getChildByType(children,ModalBody);constfooter=getChildByType(children,ModalFooter);if(!open)returnnull;returnReactDOM.createPortal(<><Boxposition="fixed"zIndex={20}top="50%"left="50%"sx={{transform:"translate(-50%, -50%)"}}boxShadow="rgba(149, 157, 165, 0.2) 0px 8px 24px;"bgcolor="white"p="1rem"borderRadius=".5rem"width="500px"><Boxdisplay="flex"alignItems="center"justifyContent="space-between"><Typographyvariant="h1"fontSize="1.5rem"fontWeight="bold">{title}</Typography><Typographyvariant="caption"onClick={onClose}> close</Typography></Box>{body}{footer}</Box><Boxposition="fixed"zIndex={10}bgcolor="rgba(0, 0, 0, 0.5)"width="100%"height="100%"top={0}left={0}/></>,document.getElementById("root"));};Modal.Body=ModalBody;Modal.Footer=ModalFooter;exportdefaultModal;
Pada kode ini:
constbody=getChildByType(children,ModalBody);constfooter=getChildByType(children,ModalFooter);
Digunakan untuk mencari komponen dengan komponen dasarnya. MisalkangetChildByType(children, ModalBody)
ini berarti saya mencari komponenModalBody
didalamchildren
.
Karenachildren
ini bisa menerima banyak sekali komponen. Oleh karena itu kita memilih komponen yang dibutuhkan saja. Inilah Compound Components.
Penggunaannya yaitu padaApp.jsx
:
import{Button,TextField}from"@mui/material";import{useState}from"react";importModalfrom"./components/modal";functionApp(){const[isOpen,setIsOpen]=useState(false);consttoggle=()=>setIsOpen((isOpen)=>!isOpen);return(<div><Modalopen={isOpen}title="Simple Modal"onClose={toggle}><Modal.Body><TextFieldplaceholder="Masukkan nama"variant="standard"/></Modal.Body><Modal.Footer><Buttonvariant="contained">Simpan</Button></Modal.Footer></Modal><Buttonvariant="contained"onClick={toggle}> Open Modal</Button></div>);}exportdefaultApp;
Hasilnya:
Sebentar 🤔 Kok bisaModalBody
bisa kepilih padahal kita menggunakanModal.Body
bukanModalBody
. Nahh ingat, pada komponen<Modal />
kita sudah membuat ini:
Modal.Body=ModalBody;Modal.Footer=ModalFooter;
Oleh karena ituModal.Body
bisa panggil
Kita coba styling sedikit yaa:modal/index.jsx
import{Box,Typography}from"@mui/material";importReactDOMfrom"react-dom";import{getChildByType}from"react-nanny";constModalBody=({children=null})=>{return(<Boxcomponent="main"my="1rem">{children}</Box>);};constModalFooter=({children=null})=>{return<footer>{children}</footer>;};constModal=({children=null,open=false,title="",onClose=()=>{},})=>{constbody=getChildByType(children,ModalBody);constfooter=getChildByType(children,ModalFooter);if(!open)returnnull;returnReactDOM.createPortal(<><Boxposition="fixed"zIndex={20}top="50%"left="50%"sx={{transform:"translate(-50%, -50%)"}}boxShadow="rgba(149, 157, 165, 0.2) 0px 8px 24px;"bgcolor="white"p="1rem"borderRadius=".5rem"width="500px"><Boxdisplay="flex"alignItems="center"justifyContent="space-between"><Typographyvariant="h1"fontSize="1.5rem"fontWeight="bold">{title}</Typography><Typographyvariant="caption"onClick={onClose}color="lightgray"> close</Typography></Box>{body}{footer}</Box><Boxposition="fixed"zIndex={10}bgcolor="rgba(0, 0, 0, 0.5)"width="100%"height="100%"top={0}left={0}/></>,document.getElementById("root"));};Modal.Body=ModalBody;Modal.Footer=ModalFooter;exportdefaultModal;
App.jsx
import{Button,TextField}from"@mui/material";import{useState}from"react";importModalfrom"./components/modal";functionApp(){const[isOpen,setIsOpen]=useState(false);consttoggle=()=>setIsOpen((isOpen)=>!isOpen);return(<div><Modalopen={isOpen}title="Login"onClose={toggle}><Modal.Body><TextFieldplaceholder="Email"variant="standard"sx={{width:"100%"}}/><TextFieldplaceholder="Password"variant="standard"type="email"sx={{width:"100%",mt:"1rem"}}/></Modal.Body><Modal.Footer><ButtononClick={toggle}variant="contained"> Login</Button></Modal.Footer></Modal><Buttonvariant="contained"onClick={toggle}> Open Modal</Button></div>);}exportdefaultApp;
Hasil nya:
Keuntungan ✨
Apa yaa keuntungan Compound Component ini? sepertinya sama aja mengguankanchildren
biasa. Keuntungannya tuh disini:
import{Button,TextField}from"@mui/material";import{useState}from"react";importModalfrom"./components/modal";functionApp(){const[isOpen,setIsOpen]=useState(false);consttoggle=()=>setIsOpen((isOpen)=>!isOpen);return(<div><Modalopen={isOpen}title="Login"onClose={toggle}><Modal.Footer><!--Footerterlebihdahulu--><ButtononClick={toggle}variant="contained"> Login</Button></Modal.Footer><Modal.Body><TextFieldplaceholder="Email"variant="standard"sx={{width:"100%"}}/><TextFieldplaceholder="Password"variant="standard"type="email"sx={{width:"100%",mt:"1rem"}}/></Modal.Body></Modal><Buttonvariant="contained"onClick={toggle}> Open Modal</Button></div> );}export default App;
Kamu bisa mengisi<Modal.Footer />
terlebih dahulu lalu<Modal.Body />
. Kalau menggunakanchildren
biasa, sudah pasti posisinya berubah. Nahh kalau menggunakan Compound Component ini meski posisi di parent nya berubah, tapi didalam komponen Compound nya tidak akan berubah
Hasilnya:
Kekurangan 🔻
Sejauh pengalaman saya, kekurangan dariCompound Components
ini adalah setup komponen yang lama. Kita harus mendefinisikan setiap bagiannya (Header, Body, dll). Jadi tetap ada kekurangannya hehe
Penutup
Mungkin itu saja pembahasan mengenai Compound Component pada Reactjs. Kalau menurutmu ini bermanfaat silahkan bagikan ke teman-teman mu yaa 😄
Sampai jumpa di tutorial React selanjutnya 👋
Oh iyaa untuk source code nya kunjungihttps://github.com/alfianandinugraha/modal-compound
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse