Table of Contents

Pengenalan

Hooks merupakan fitur baru di React 16.8. Dengan Hooks, kita dapat menggunakan state dan fitur React yang lain tanpa perlu menulis sebuah kelas baru. Salah satunya yaitu useState diperkenalkan melalui kode di bawah ini.

				
					import React, { useState } from 'react';

function Example() {
  // Mendeklarasikan variabel state baru, yaitu "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Anda mengklik {count} kali</p>
      <button onClick={() => setCount(count + 1)}>
        Klik aku
      </button>
    </div>
  );
}
				
			
useState
Penggunaan Sederhana

Dengan referensi kode di atas useState merupakan Hook. useState di panggil dalam function component untuk menambahkan suatu state lokal. React akan menyimpan state antar render. useState memberikan dua hal:

  1. nilai state saat ini(count)
  2. fungsi untuk memperbarui nilai tersebut(setCount)

 Anda dapat memanggil fungsi ini dari sebuah event handler atau dimanapun. Hal ini serupa dengan this.setState pada kelas, tetapi tidak menggabungkan state lama dan baru menjadi satu.

Mendeklarasikan Beberapa Variable State

Anda dapat menggunakan Hook untuk state lebih dari sekali pada satu komponen:

				
					function ContohDenganBanyakState() {
  // Deklarasi banyak variabel state!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('pisang');
  const [todos, setTodos] = useState([{ text: 'Belajar Hooks' }]);
  // ...
}
				
			

Tidak hanya variable, useState juga dapat berisi array.

Menambah Element Array

Sebelum mempelajari cara memanipulasi Array dengan useState kita harus mendefiniskannya terlebih dahulu.

				
					import React, { useState } from 'react';

export default function Example() {
    const [todo, setTodo]= useState("")
    const [todos, setTodos] = useState([]);

    const addTodos = (e) => {
        setTodos(prevTodo => [...prevTodo, todo]);
    }

    return (
        <div>
            <ul>
                {todos.map((row,key)=>
                    <li>{row}</li>
                )}
            </ul>
        <input type="text" value={todo} onChange={e => setTodo(e.target.value)} />
        <button onClick={() => addTodos()}>
            Tambah Array
        </button>
        </div>
    );
}
				
			

todos.push(todo) salah

setTodos(prevTodos => […prevTodo, todo] ) benar

Menambah Object dalam Array Of Object
				
					import { useState } from "react";

const App = () => {
    const [friends, setFriends] = useState(friendsArray);

    const handleAddFriend = () => {
        setFriends((prevFriends) => [
            ...prevFriends, {
                name: "Random Friend Name",
                age: 20, 
            },
        ]);
    };

    return (
        <main>
            <ul>
                {friends.map((friend, index) => (
                    <li key={index}>
                        <span>name: {friend.name}</span>{" "}
                        <span>age: {friend.age}</span>
                    </li>
                ))}
                <button onClick={handleAddFriend}>Add Friends</button>
            </ul>
        </main>
    );
};

export default App;

const friendsArray = [
    {
        name: "John",
        age: 19,
    },
    {
        name: "Candy",
        age: 18,
    },
    {
        name: "mandy",
        age: 20,
    },
];
				
			
Mengubah Object dalam Array Of Object
				
					import { useState } from "react";

const App = () => {
    const [friends, setFriends] = useState(friendsArray);    

    const handleSecondFriend = () => {        
        setFriends(
            friends.map((friend) =>
                // Here you accept a id argument to the function and replace it with hard coded 🤪 2, to make it dynamic.
                friend.id === 2
                    ? { ...friend, name: "Changed Name" }
                    : { ...friend }
            )
        );
    };

    return (
        <main>
            <ul>
                {friends.map((friend, index) => (
                    <li key={index}>
                        <span>name: {friend.name}</span>{" "}
                        <span>age: {friend.age}</span>
                    </li>
                ))}
                <button onClick={handleSecondFriend}>Change Second Name</button>
            </ul>
        </main>
    );
};

export default App;

const friendsArray = [
    {
        id:1,
        name: "John",
        age: 19,
    },
    {
        id:2,
        name: "Candy",
        age: 18,
    },
    {
        id:3,
        name: "mandy",
        age: 20,
    },
];
				
			
Menghapus Object dalam Array Of Object
				
					import { useState } from "react";

const App = () => {
    const [friends, setFriends] = useState(friendsArray);    

    const removeFriendByName = () => {
        setFriends((current) => current.filter((friend)=>friend.name !== "Candy"));
    }

    return (
        <main>
            <ul>
                {friends.map((friend, index) => (
                    <li key={index}>
                        <span>name: {friend.name}</span>{" "}
                        <span>age: {friend.age}</span>
                    </li>
                ))}
                <button onClick={removeFriendByName}>Remove Candy</button>
            </ul>
        </main>
    );
};

export default App;

const friendsArray = [
    {
        id:1,
        name: "John",
        age: 19,
    },
    {
        id:2,
        name: "Candy",
        age: 18,
    },
    {
        id:3,
        name: "mandy",
        age: 20,
    },
];
				
			
useEffect

Hook useEffect, menambahkan kemampuan untuk melakukan “efek samping” dari sebuah function component. Hook ini memiliki fungsi yang sama dengan componentDidMount, componentDidUpdate, dan componentDidMount pada kelas React, tetapi disatukan menjadi satu fungsi.

Sebagai contoh, komponen berikut menetapkan judul dokumen setelah React memperbarui DOM:

				
					import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Sama seperti componentDidMount dan componentDidUpdate:
  useEffect(() => {
    // Memperbarui judul dokumen menggunakan API browser
    document.title = `Anda klik sebanyak ${count} kali`;
  });

  return (
    <div>
      <p>Anda klik sebanyak {count} kali</p>
      <button onClick={() => setCount(count + 1)}>
        Klik saya
      </button>
    </div>
  );
}
				
			

Jalankan pada browser, anda akan melihat setiap ada perubahan pada variable count, maka document title anda akan ikut berubah, sama seperti componentDidUpdate.

Sekarang ubah kodenya menjadi :

				
					useEffect(() => {
    // Memperbarui judul dokumen menggunakan API browser
    document.title = `Anda klik sebanyak ${count} kali`;
  ,[]},
				
			

Setelah dijalankan dokumen title hanya berubah sekali menjadi “Anda klik sebanyak 0 kali” dan berhenti disitu walaupun button terus di Klik, berarti useEffect hanya berjalan 1 kali saat pertama kali dijalankan mirip seperti componentDidMount.

Nah untuk penerapan componentWillUnmount kita akan membuat kode yang berbeda untuk simulasinya. Berikut kodenya

				
					import { useState, useEffect } from 'react';

export default function App() {
    const [boolean,setBoolean] = useState(true);
    const toggleBoolean = () => setBoolean((prevState) => !prevState);

    return(
      <div>
       { boolean ?
           <Component1/>
         : <Component2/>
       }
         <button onClick={toggleBoolean}>Toggle</button>
       </div>
    );
}
  
function Component1() {
    useEffect(() => {
      console.log("Component1 has mounted...");
      return () => { console.log("Component1 has unmounted...")};
    },[]);
  
    return(
      <div>Component1</div>
    );
}
  
function Component2() {
    return(
      <div>Component2</div>
    );
}
				
			

Jalankan, lalu buka Developer Tool tab Console. Akan muncul log “Component 1 mounted”… saat pertama kali dijalankan, lalu saat Toggle di klik Componen 1 akan hilang dan Component 2 akan mucul, perhatikan tab Console akan muncul log “Component 1 unmounted”

useContext

Ada dua fungsi yang akan kita gunakan yaitu :

  1. createContext
  2. useContext

Contoh penerapannya sebagai berikut

				
					import { useState, createContext, useContext } from "react";

const UserContext = createContext();

function Component1() {
  const [user, setUser] = useState("Jesse Hall");

  return (
    <UserContext.Provider value={user}>
      <h1>{`Hello ${user}!`}</h1>
      <Component2 />
    </UserContext.Provider>
  );
}

function Component2() {
  return (
    <>
      <h1>Component 2</h1>
      <Component3 />
    </>
  );
}

function Component3() {
  return (
    <>
      <h1>Component 3</h1>
      <Component4 />
    </>
  );
}

function Component4() {
  return (
    <>
      <h1>Component 4</h1>
      <Component5 />
    </>
  );
}

function Component5() {
  const user = useContext(UserContext);

  return (
    <>
      <h1>Component 5</h1>
      <h2>{`Hello ${user} again!`}</h2>
    </>
  );
}

export default Component1
				
			
useRef

Hooks useRef umunnya digunakan untuk mengakses DOM node dalam sebuah komponen. Misal kita mau mengakses element input, maka kita bisa menambahkan ref ke element inputnya.

Perhatikan contoh berikut.

				
					import React, { useRef, useEffect } from "react"

export default function Example() {
    const inputRef = useRef()
    useEffect(() => {
        inputRef.current.value="Anjay"
    },[])
    
    return(
        <div>
            <input value="" type="text" ref={inputRef} placeholer="Text 1"/>
        </div>
    )
}
				
			

Jika dijalankan value input akan otomatis terisi Anjay.

useCallback

Hook useCallback dapat digunakan untuk mencegah render child komponen yang tidak di perlukan.

Sebelum memahami useCallback ada baiknya kita memahami React.memo dimana fungsinya yaitu memberikan pengoptimalan terhadap performa React, dimana component hanya akan melakukan render ulang jika props atau state-nya berubah. Contohnya sebagai berikut.

React.memo
				
					import { useState } from "react";

const App = () => {
  console.log('parent render')
  const [count, setCount] = useState(0);
  const [todos,setTodo] = useState(["todo 1","todo 2"]);

  const increment = () => {
    setCount((c) => c + 1);
  };

  return (
    <>
      <Todos todos={todos} name="Bagus" />
      <hr />
      <div>
          Count: {count}<br/>
          <button onClick={increment}> + (parent punya) </button>
          <button onClick={()=>{setTodo(todos=>[...todos,`todo ${todos.length+1}`])}}>Add (child punya)</button>
      </div>
    </>
  );
};

const Todos = ({todos,name}) => {
  console.log("child render");
  return (
    <>
      <h2>Tugas si {name}</h2>
      {todos.map((todo, index) => {
        return <p key={index}>{todo}</p>;
      })}
    </>
  );
};
export default App
				
			

Jalankan lalu perhatikan Console browser anda.

Setiap kali tombol + di klik child(Todos) ikut dirender. Bayangkan jika kita memiliki banyak Component dalam 1 page aplikasi, semuanya ikut di render ulang.

Nah.. untuk mengatasi masalah tersebut kita gunakan React.memo untuk melindungi si Component dari render parent dengan cara sebagai berikut.

				
					...
import { memo } from "react";
...
const Todos = memo(({todos,name}) => {
...
});
...
				
			

Lalu jalankan kembali.

Dapat dilihat bedanya child tidak dirender ulang.

React.memo vs UserCallback

Kita akan langsung membuat simulasinya kembali kode berikut akan memiliki kemiripan dengan contoh penerapan React.memo sebelumnya.

				
					import { useState, memo } from "react";

const App = () => {
    console.log("parent");
    const [count, setCount] = useState(0);
    const [todos, setTodos] = useState([]);

    const increment = () => {
        setCount((c) => c + 1);
    };
    const addTodo = () => {
        setTodos((t) => [...t, "New Todo"]);
    };
    

    return (<>
        <Todos todos={todos} addTodo={addTodo} />
        <hr />
        <div>
            Count: {count}
            <button onClick={increment}>+</button>
        </div>
    </>);
};

const Todos = memo(({ todos, addTodo }) => {
    console.log("child render");
    return (<>
        <h2>My Todos</h2>
        {todos.map((todo, index) => {
          return <p key={index}>{todo}</p>;
        })}
        <button onClick={addTodo}>Add Todo</button>
    </>);
});

export default App
				
			

Lalu jalankan…

Kamu akan melihat bahwa komponen Todos merender ulang meskipun todos tidak berubah.

Mengapa ini tidak berhasil? Padahal sudah menggunakan React.memo, jadi komponen Todos seharusnya tidak dirender ulang karena status todos maupun fungsi addTodo tidak berubah saat hitungan bertambah.

Hal ini karena sesuatu yang disebut Kesetaraan Referensial.

Setiap kali Component merender ulang, fungsinya dibuat ulang. Karena itu, fungsi addTodo sebenarnya telah berubah.

Bagaimana solusinya, disitulah useCallback berperan kita dapat menggunakannya untuk mencegah fungsi dibuat ulang kecuali diperlukan.

				
					import { useState, useCallback } from "react";
...
    const addTodo = useCallback(() => {
        setTodos((t) => [...t, "New Todo"]);
    },[]);
...
				
			

Lalu jalankan kembali. Child tidak akan dirender ulang.

useMemo

Hooks useMemo digunakan untuk mengoptimalkan performance sebuah function. Misal untuk memoize hasil sebuah function. Memoize adalah sebuah teknik yang digunakan mengoptimalkan sebuah program atau istilah kerennya caching.

Dalam contoh berikut, aplikasi memiliki fungsi yang cukup berat yang nantinya akan berjalan pada setiap render. Saat mengubah hitungan atau menambahkan todo, Anda akan melihat delay eksekusi.

				
					import { useState } from "react";

const App = () => {
    const [count, setCount] = useState(0);
    const [todos, setTodos] = useState([]);
    const calculation = expensiveCalculation(count);

    const increment = () => {
        setCount((c) => c + 1);
    };
    const addTodo = () => {
        setTodos((t) => [...t, "New Todo"]);
    };

    return (
        <div>
        <div>
            <h2>My Todos</h2>
            {todos.map((todo, index) => {
                return <p key={index}>{todo}</p>;
            })}
            <button onClick={addTodo}>Add Todo</button>
        </div>
        <hr />
        <div>
            Count: {count}
            <button onClick={increment}>+</button>
            <h2>Expensive Calculation</h2>
            {calculation}
        </div>
        </div>
    );
};

const expensiveCalculation = (num) => {
    console.log("Calculating...");
    for (let i = 0; i < 1000000000; i++) {
        num += 1;
    }
    console.log("Finish");
    return num;
};

export default App
				
			

Jalankan lalu perhatikan Console Browser anda setiap anda mengklik tombol Add Todo seharusnya ada jeda beberapa detik.

Tergantung komputer anda kalo teda tidak nampak tambahkan angka 0 lagi dibelakang 1000000000.

Dari kode di atas seharusnya fungsi tidak perlu dirender/dijalankan ulang saat menjalankan tombol Add Todo karena hasilnya akan tetap sama, kecuali anda mengklik tombol +

Namun aturan dari reactjs adalah setiap kali terjadi update state baik todo maupun count, maka Component akan dirender ulang.

Untuk mengatasinya kita bisa menggunakan useMemo untuk mencaching hasil dari function expensiveCalculation.

				
					import { useState, useMemo } from "react";
...
    const calculation = useMemo(() => expensiveCalculation(count), [count]);
...
				
			

Lalu jalankan kembali.