React Hooks: useContext()
-
Use Context
Berbicara tentang
context
, simpelnya context ini adalah global state manajemen bawaan React. Dengan menggunakancontext
, kita bisa mempasing data/mengirim data dari parent komponen ke child komponen tanpa harus menggunakanprops
untuk melewati level2nya.Di dokumentasi React sendiri sempat disinggung mengenai
component tree
, ini menjadi maksud bahwa cara berfikir React, adalahstruktur hierarki
, yang artinya komponen terbagi2 menjadi susunan kecil piece by piece, sub by sub atau level by level. Perumpamaannya seperti ini, ada komponen orang tua dan ada komponen anak-anak:- FilterableProductTable
- SearchBar
- ProductTable
- ProductCategoryRow
- ProductRow
React menggunakan konsep props dan state untuk berinteraksi antara komponen dengan proses inputan dan data. Konsep yang cerdas menurut saya, tapi masalahnya adalah ada beberapa pertanyaan 'apakah state ini berlaku di semua komponen? Atau hanya di komponen itu sendiri saja?, Ketika komponen saya nantinya sudah banyak cabang2, banyak melalui level sampai 5 level, apa saya harus menggunakan props untuk sharing input terus menerus?'
Jika kita membaca dokumentasi, React memberikan beberapa solusi tepat dari masalah diatas. 'If you only want to avoid passing some props through many levels, component composition is often a simpler solution than context.' artinya kalau cuman untuk menghindari melewatkan beberapa props melalui banyak level, kita bisa menggunakan komposisi komponen ini cara yang lebih mudah daripada menggunakan context.
Namun, bila tujuannya untuk sharing data antar komponen yang sifatnya global seperti: authentikasi user, tema, cache data, dan language, maka ini adalah cara yang tepat untuk menggunakan
context
. Inipun kita juga harus hati-hati dan tepat dalam menggunakannya 'apply it sparingly because it makes component reuse more difficult.'.Component Composition
Ini sebenernya merupakan sebuah komponen pattern yang digunakan untuk membangun komponen dari komponen lain menjadi kesatuan yang lebih besar, istilahnya seperti gotong royong. Tetapi, konsep ini punya aturan, casenya seperti ini anggap saja dalam gotong royong terdapat 2 role, role sebagai ketua kelompok dan role sebagai anggota.
Aturannya adalah ketika para anggota saling bahu membahu untuk membawa box(misal) maka anggota lain tidak perlu tahu isi box tersebut apa. Tugasnya anggota hanya membawa box tersebut sampai ke tujuan. Sedangkan ketua kelompok tugasnya mengawasi dan menyiapkan box supaya siap dikirim ke pelanggan, seperti *label, rincian, dll`.
Jika di realitakan dalam bentuk kode, contohnya seperti dibawah ini:
function Anggota(props){ return <span>{ props.text }</span> } function Anggota_Dua(props){ return <button>{ props.render }</button> // Cuman sebagai terusan untuk merender <Anggota/> } function KetuaKelompok(){ const btnText = <Anggota text="Click Me" />; return <Anggota_Dua render={btnText}/> } <KetuaKelompok/>
Pada intinya komposisi komponen ini tujuannya adalah sebagai terusan yang dipassing/dikirim melalui
props
. Mirip seperti konsep{ props.children }
, lebih jelasnya bisa lihat example. Cara ini lebih tepat dan mudah dibandingkan dengancontext
Context
Seperti yang dijelaskan diatas, context adalah global state management. Sebenernya harusnya disini saya langsung saja menjelaskan bagaimana cara dan konsep
useContext()
hooks, cuman karena ada beberapa proses yang berhubungan jadi saya akan jelaskan secara detail.Pada beberapa source biasanya menggunakan useReducer() untuk melakukan manipulasi state, padahal case yang dialami hanya seperti dibawah ini:
// Reducer export const Reducer = (state = initialState) => { ... case StepActionStatus.ADD_FORM_DATA_PREV: { return { ...state, storedFormData: [...state.storedFormData.concat(action.payload.allFields)].reverse().sort() } } ... }
Pointnya tujuan dari kode itu adalah hanya untuk menggabungkan data di store dengan payload (context), tentunya cara ini malah menyulitkan, kalau ada cara yang lebih mudah kenapa cari yang sulit:). Maka dari itu, selagi casenya ga kompleks2 banget, pake
useState()
aja udah cukup (bahkan ini cara yang lebih tepat).Ini adalah cara best praktis yang umumnya sering kali digunakan, yaitu dengan menggunakan suatu
fungsi
untuk melakukan perubahan nilai padacontext
. Disini saya buat 2 versi, yaitu dengan menggunakan pureuseState
, dan dengan bantuanuseCallback
, Perhatikan kode dibawah ini:const initialState = { state: '' } export const Context = React.createContext<typeof initialState | any>(undefined); function ContextProvider(props: React.PropsWithChildren<{}>) { const [state, setState] = React.useState(initialState); return ( <Context.Provider value={{ state, setState }}> {props.children} </Context.Provider> ) } export default ContextProvider; const contextState = React.useContext(Context); const { setState } = contextState;
Pada kode diatas, value pada
Context.Provider
diambil dari local state, jadi ketika context ini di use maka value yg didapat adalahstate
&setState
. Seperti yang dijelaskan pada module sebelumnya,[state, setState]
variabel pertama untuk nilai, variabel kedua untuk merubah nilai.Untuk cara yang kedua, hampir mirip dengan cara yang pertama, cuman bedanya ditambah
useCallback
, perhatikan kode dibawah ini:const initialStateCallback = { callbackState: '' } export const ContextCallback = React.createContext<typeof initialStateCallback | any>(undefined); function ContextCallbackProvider(props: React.PropsWithChildren<{}>) { const [callbackState, setCallbackState] = React.useState(initialStateCallback); // With useCallback (prefered) const setContextCallback = React.useCallback( newState => { return setCallbackState({ callbackState: { ...newState } }) }, [callbackState, setCallbackState] ); const getContextCallback = React.useCallback( () => ({ setContextCallback, ...callbackState }), [callbackState, setContextCallback] ); return ( <ContextCallback.Provider value={getContextCallback()}> {props.children} </ContextCallback.Provider> ) } export default ContextCallbackProvider; const contextCallbackState = React.useContext<any>(ContextCallback); const { setContextCallback } = contextCallbackState;
Kamu juga bisa menginitialize
useCallback()
nya di komponennya langsung cuman saya lebih prefer di providernya sih. Contoh kodenya seperti ini:const handlerCallback = React.useCallback(() => { setState({ state: 'Transform to useCallback()', }) }, [contextState.state, setState]); <button onClick={handlerCallback}>Click Me</button>
Alasannya mengapa menggunakan
useCallback()
adalah karena ini 'Using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.' artinya arrow function() => { ... }
ini akan membuat fungsi baru setiap kali komponen dirender ulang, ini bisa menyebabkan memory leaks.Next Hooks
Lihat versi full module penggunaan hooks secara lengkap dalam Bahasa Indonesia
https://github.com/natserract/react-hooks-deepdive. Kamu juga bisa ikut berkontribusi, pull request are welcome - FilterableProductTable
Pengumuman!
Untuk yang baru join, jangan lupa perkenalkan dirimu disini ya
Juga jangan lupa baca ketentuan penggunaan di forum ini. Rekan-rekan bisa lihat disini.
Buat yang penasaran alasan dibuatnya forum BaliJS ini silakan baca disini.