React Hooks: useContext()

Topic created ยท 1 Posts ยท 23 Views
  • Use Context

    Berbicara tentang context, simpelnya context ini adalah global state manajemen bawaan React. Dengan menggunakan context, kita bisa mempasing data/mengirim data dari parent komponen ke child komponen tanpa harus menggunakan props untuk melewati level2nya.

    Di dokumentasi React sendiri sempat disinggung mengenai component tree, ini menjadi maksud bahwa cara berfikir React, adalah struktur 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 dengan context

    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 pada context. Disini saya buat 2 versi, yaitu dengan menggunakan pure useState, dan dengan bantuan useCallback, 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 adalah state & 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 ๐Ÿ˜‰

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.

Utas Populer Bulan Ini

Semeton Online

Forum Stats

0
Online

122
Users

91
Topics

456
Posts