React Hooks Berguna Yang Dapat Anda Gunakan Dalam Proyek Anda

Diterbitkan: 2022-03-10
Ringkasan cepat Komponen berbasis kelas React berantakan, membingungkan, sulit bagi manusia dan mesin. Tetapi sebelum React 16.8, komponen berbasis kelas wajib untuk setiap proyek yang memerlukan status, metode siklus hidup, dan banyak fungsi penting lainnya. Semua ini berubah dengan diperkenalkannya hook di React 16.8. Hooks adalah pengubah permainan. Mereka telah menyederhanakan React, membuatnya lebih rapi, lebih mudah untuk menulis dan debug, dan juga mengurangi kurva belajar.

Hooks hanyalah fungsi yang memungkinkan Anda untuk terhubung atau menggunakan fitur React. Mereka diperkenalkan di React Conf 2018 untuk mengatasi tiga masalah utama komponen kelas: neraka pembungkus, komponen besar, dan kelas yang membingungkan. Hooks memberi kekuatan pada komponen fungsional React, sehingga memungkinkan untuk mengembangkan seluruh aplikasi dengannya.

Masalah-masalah komponen kelas yang disebutkan di atas terhubung dan memecahkan satu tanpa yang lain dapat menimbulkan masalah lebih lanjut. Untungnya, hook menyelesaikan semua masalah dengan sederhana dan efisien sambil menciptakan ruang untuk fitur yang lebih menarik di React. Hooks tidak menggantikan konsep dan kelas React yang sudah ada, mereka hanya menyediakan API untuk mengaksesnya secara langsung.

Tim React memperkenalkan beberapa kait di React 16.8. Namun, Anda juga dapat menggunakan kait dari penyedia pihak ketiga di aplikasi Anda atau bahkan membuat kait khusus. Dalam tutorial ini, kita akan melihat beberapa hook yang berguna di React dan cara menggunakannya. Kami akan membahas beberapa contoh kode dari setiap kait dan juga menjelajahi cara Anda membuat kait khusus.

Catatan: Tutorial ini membutuhkan pemahaman dasar tentang Javascript (ES6+) dan React.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Motivasi Di Balik Kait

Seperti yang dinyatakan sebelumnya, kait dibuat untuk memecahkan tiga masalah: neraka pembungkus, komponen besar, dan kelas yang membingungkan. Mari kita lihat masing-masing secara lebih rinci.

Neraka Pembungkus

Aplikasi kompleks yang dibangun dengan komponen kelas dengan mudah mengalami pembungkus neraka. Jika Anda memeriksa aplikasi di React Dev Tools, Anda akan melihat komponen yang sangat bersarang. Ini membuatnya sangat sulit untuk bekerja dengan komponen atau men-debug mereka. Meskipun masalah ini dapat diselesaikan dengan komponen tingkat tinggi dan render props , mereka mengharuskan Anda untuk sedikit memodifikasi kode Anda. Hal ini dapat menyebabkan kebingungan dalam aplikasi yang kompleks.

Kait mudah dibagikan, Anda tidak perlu memodifikasi komponen sebelum menggunakan kembali logika.

Contoh bagusnya adalah penggunaan Redux connect Higher Order Component (HOC) untuk berlangganan toko Redux. Seperti semua HOC, untuk menggunakan connect HOC, Anda harus mengekspor komponen di samping fungsi tingkat tinggi yang ditentukan. Dalam kasus connect , kita akan memiliki sesuatu dalam bentuk ini.

 export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

Di mana mapStateToProps dan mapDispatchToProps adalah fungsi yang akan ditentukan.

Sedangkan di era Hooks, seseorang dapat dengan mudah mencapai hasil yang sama dengan rapi dan ringkas dengan menggunakan Redux useSelector dan useDispatch hooks.

Komponen Besar

Komponen kelas biasanya mengandung efek samping dan logika stateful. Saat aplikasi tumbuh dalam kompleksitas, biasanya komponen menjadi berantakan dan membingungkan. Ini karena efek samping diharapkan diatur oleh metode siklus hidup daripada fungsionalitas. Meskipun dimungkinkan untuk membagi komponen dan membuatnya lebih sederhana, ini sering kali memperkenalkan tingkat abstraksi yang lebih tinggi.

Kait mengatur efek samping berdasarkan fungsionalitas dan dimungkinkan untuk membagi komponen menjadi beberapa bagian berdasarkan fungsionalitas.

Kelas yang Membingungkan

Kelas umumnya merupakan konsep yang lebih sulit daripada fungsi. Komponen berbasis kelas React bertele-tele dan agak sulit untuk pemula. Jika Anda baru mengenal Javascript, Anda dapat menemukan fungsi lebih mudah untuk memulai karena sintaksnya yang ringan dibandingkan dengan kelas. Sintaksnya bisa membingungkan; terkadang, adalah mungkin untuk melupakan pengikatan event handler yang dapat merusak kode.

React memecahkan masalah ini dengan komponen fungsional dan kait, memungkinkan pengembang untuk fokus pada proyek daripada sintaks kode.

Misalnya, dua komponen React berikut akan menghasilkan hasil yang persis sama.

 import React, { Component } from "react"; export default class App extends Component { constructor(props) { super(props); this.state = { num: 0 }; this.incrementNumber = this.incrementNumber.bind(this); } incrementNumber() { this.setState({ num: this.state.num + 1 }); } render() { return ( <div> <h1>{this.state.num}</h1> <button onClick={this.incrementNumber}>Increment</button> </div> ); } }
 import React, { useState } from "react"; export default function App() { const [num, setNum] = useState(0); function incrementNumber() { setNum(num + 1); } return ( <div> <h1>{num}</h1> <button onClick={incrementNumber}>Increment</button> </div> ); }

Contoh pertama adalah komponen berbasis kelas sedangkan yang kedua adalah komponen fungsional. Meskipun ini adalah contoh sederhana, perhatikan betapa palsunya contoh pertama dibandingkan dengan contoh kedua.

Konvensi Dan Aturan Kait

Sebelum mempelajari berbagai kait, akan sangat membantu untuk melihat konvensi dan aturan yang berlaku untuknya. Berikut adalah beberapa aturan yang berlaku untuk hook.

  1. Konvensi penamaan hook harus dimulai dengan awalan use . Jadi, kita dapat memiliki useState , useEffect , dll. Jika Anda menggunakan editor kode modern seperti Atom dan VSCode, plugin ESLint bisa menjadi fitur yang sangat berguna untuk hook React. Plugin ini memberikan peringatan dan petunjuk yang berguna tentang praktik terbaik.
  2. Kait harus dipanggil di tingkat atas komponen, sebelum pernyataan kembali. Mereka tidak dapat dipanggil di dalam pernyataan bersyarat, loop, atau fungsi bersarang.
  3. Hook harus dipanggil dari fungsi React (di dalam komponen React atau hook lain). Seharusnya tidak dipanggil dari fungsi Vanilla JS.

useState Keadaan Penggunaan

Kait useState adalah kait React yang paling dasar dan berguna. Seperti kait bawaan lainnya, kait ini harus diimpor dari react untuk digunakan dalam aplikasi kita.

 import {useState} from 'react'

Untuk menginisialisasi status, kita harus mendeklarasikan status dan fungsi pembarunya dan memberikan nilai awal.

 const [state, updaterFn] = useState('')

Kita bebas untuk memanggil fungsi state dan updater apapun yang kita inginkan tetapi dengan konvensi, elemen pertama dari array akan menjadi state kita sedangkan elemen kedua akan menjadi fungsi updater. Ini adalah praktek umum untuk awalan fungsi updater kami dengan set awalan diikuti dengan nama negara bagian kami dalam bentuk kasus unta.

Misalnya, mari kita atur status untuk menyimpan nilai hitungan.

 const [count, setCount] = useState(0)

Perhatikan bahwa nilai awal dari status count kita diatur ke 0 dan bukan string kosong. Dengan kata lain, kita dapat menginisialisasi status kita ke segala jenis variabel JavaScript, yaitu angka, string, boolean, array, objek, dan bahkan BigInt. Ada perbedaan yang jelas antara status pengaturan dengan kait useState dan status komponen berbasis kelas. Patut dicatat bahwa kait useState mengembalikan sebuah array, juga dikenal sebagai variabel status dan dalam contoh di atas, kita mendestruktur array menjadi state dan fungsi updater .

Rendering Komponen

Menyetel status dengan kait useState menyebabkan komponen yang sesuai dirender. Namun, ini hanya terjadi jika React mendeteksi perbedaan antara state sebelumnya atau lama dan state baru. React melakukan perbandingan status menggunakan algoritma Javascript Object.is .

Mengatur Status Dengan useState

Status count kami dapat disetel ke nilai status baru hanya dengan meneruskan nilai baru ke fungsi setCount updater sebagai berikut setCount(newValue) .

Metode ini berfungsi saat kita tidak ingin mereferensikan nilai status sebelumnya. Jika kita ingin melakukan itu, kita perlu meneruskan sebuah fungsi ke fungsi setCount .

Dengan asumsi kami ingin menambahkan 5 ke variabel count kami kapan saja sebuah tombol diklik, kami dapat melakukan hal berikut.

 import {useState} from 'react' const CountExample = () => { // initialize our count state const [count, setCount] = useState(0) // add 5 to to the count previous state const handleClick = () =>{ setCount(prevCount => prevCount + 5) } return( <div> <h1>{count} </h1> <button onClick={handleClick}>Add Five</button> </div> ) } export default CountExample

Dalam kode di atas, pertama-tama kita mengimpor kait useState dari react dan kemudian menginisialisasi status count dengan nilai default 0. Kami membuat handler onClick untuk menaikkan nilai count sebanyak 5 setiap kali tombol diklik. Kemudian kami menampilkan hasilnya dalam tag h1 .

Mengatur Array Dan Status Objek

Status untuk array dan objek dapat diatur dengan cara yang sama seperti tipe data lainnya. Namun, jika kita ingin mempertahankan nilai yang sudah ada, kita perlu menggunakan operator spread ES6 saat menyetel status.

Operator spread dalam Javascript digunakan untuk membuat objek baru dari objek yang sudah ada. Ini berguna di sini karena React membandingkan keadaan dengan operasi Object.is dan kemudian merender sesuai.

Mari kita pertimbangkan kode di bawah ini untuk mengatur status pada klik tombol.

 import {useState} from 'react' const StateExample = () => { //initialize our array and object states const [arr, setArr] = useState([2, 4]) const [obj, setObj] = useState({num: 1, name: 'Desmond'}) // set arr to the new array values const handleArrClick = () =>{ const newArr = [1, 5, 7] setArr([...arr, ...newArr]) } // set obj to the new object values const handleObjClick = () =>{ const newObj = {name: 'Ifeanyi', age: 25} setObj({...obj, ...newObj}) } return( <div> <button onClick ={handleArrClick}>Set Array State</button> <button onClick ={handleObjClick}>Set Object State</button> </div> ) } export default StateExample

Dalam kode di atas, kami membuat dua status arr dan obj , dan menginisialisasinya ke beberapa nilai array dan objek masing-masing. Kami kemudian membuat penangan onClick yang disebut handleArrClick dan handleObjClick untuk mengatur status larik dan objek masing-masing. Saat handleArrClick diaktifkan, kami memanggil setArr dan menggunakan operator spread ES6 untuk menyebarkan nilai array yang sudah ada dan menambahkan newArr ke dalamnya.

Kami melakukan hal yang sama untuk handler handleObjClick . Di sini kami memanggil setObj , menyebarkan nilai objek yang ada menggunakan operator spread ES6, dan memperbarui nilai name dan age .

Async Nature Of useState

Seperti yang telah kita lihat, kita menyetel status dengan useState dengan meneruskan nilai baru ke fungsi updater. Jika pembaru dipanggil beberapa kali, nilai baru akan ditambahkan ke antrian dan rendering ulang dilakukan dengan menggunakan perbandingan JavaScript Object.is .

Status diperbarui secara asinkron. Ini berarti bahwa status baru pertama kali ditambahkan ke status tertunda dan setelah itu, status diperbarui. Jadi, Anda mungkin masih mendapatkan nilai status lama jika Anda mengakses status segera setelah disetel.

Mari kita perhatikan contoh berikut untuk mengamati perilaku ini.

Dalam kode di atas, kami membuat status count menggunakan kait useState . Kami kemudian membuat handler onClick untuk meningkatkan status count setiap kali tombol diklik. Perhatikan bahwa meskipun status count meningkat, seperti yang ditampilkan di tag h2 , status sebelumnya masih dicatat di konsol. Ini karena sifat hook yang asinkron.

Jika kita ingin mendapatkan status baru, kita dapat menanganinya dengan cara yang sama seperti kita menangani fungsi async. Berikut adalah salah satu cara untuk melakukannya.

Di sini, kami menyimpan newCountValue yang dibuat untuk menyimpan nilai hitungan yang diperbarui dan kemudian mengatur status count dengan nilai yang diperbarui. Kemudian, kami mencatat nilai hitungan yang diperbarui di konsol.

useEffect Efek Penggunaan

useEffect adalah kait React penting lainnya yang digunakan di sebagian besar proyek. Ia melakukan hal yang mirip dengan metode siklus hidup componentDidMount , componentWillUnmount , dan componentDidUpdate berbasis kelas. useEffect memberi kita kesempatan untuk menulis kode imperatif yang mungkin memiliki efek samping pada aplikasi. Contoh efek tersebut termasuk logging, langganan, mutasi, dll.

Pengguna dapat memutuskan kapan useEffect akan berjalan, namun jika tidak disetel, efek samping akan berjalan pada setiap rendering atau rendering.

Perhatikan contoh di bawah ini.

 import {useState, useEffect} from 'react' const App = () =>{ const [count, setCount] = useState(0) useEffect(() =>{ console.log(count) }) return( <div> ... </div> ) }

Pada kode di atas, kita cukup login count di useEffect . Ini akan berjalan setelah setiap render komponen.

Terkadang, kita mungkin ingin menjalankan hook sekali (di mount) di komponen kita. Kita dapat mencapai ini dengan menyediakan parameter kedua untuk useEffect kait Efek.

 import {useState, useEffect} from 'react' const App = () =>{ const [count, setCount] = useState(0) useEffect(() =>{ setCount(count + 1) }, []) return( <div> <h1>{count}</h1> ... </div> ) }

Kait useEffect memiliki dua parameter, parameter pertama adalah fungsi yang ingin kita jalankan sedangkan parameter kedua adalah larik dependensi. Jika parameter kedua tidak tersedia, hook akan terus berjalan.

Dengan melewatkan braket persegi kosong ke parameter kedua hook, kita menginstruksikan React untuk menjalankan hook useEffect hanya sekali, di mount. Ini akan menampilkan nilai 1 dalam tag h1 karena hitungan akan diperbarui sekali, dari 0 ke 1, saat komponen dipasang.

Kami juga dapat membuat efek samping kami berjalan setiap kali beberapa nilai dependen berubah. Ini dapat dilakukan dengan melewatkan nilai-nilai ini dalam daftar dependensi.

Misalnya, kita bisa membuat useEffect berjalan setiap kali count berubah sebagai berikut.

 import { useState, useEffect } from "react"; const App = () => { const [count, setCount] = useState(0); useEffect(() => { console.log(count); }, [count]); return ( <div> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; export default App;

useEffect di atas akan berjalan ketika salah satu dari dua kondisi ini terpenuhi.

  1. Saat dipasang — setelah komponen dirender.
  2. Ketika nilai count berubah.

Saat dipasang, ekspresi console.log akan berjalan dan log count ke 0. Setelah count diperbarui, kondisi kedua terpenuhi, sehingga useEffect berjalan lagi, ini akan berlanjut setiap kali tombol diklik.

Setelah kami memberikan argumen kedua ke useEffect , diharapkan kami meneruskan semua dependensi ke sana. Jika Anda telah menginstal ESLINT , ini akan menampilkan kesalahan serat jika ada ketergantungan yang tidak diteruskan ke daftar parameter. Ini juga bisa membuat efek samping berperilaku tidak terduga, terutama jika tergantung pada parameter yang tidak dilewati.

Membersihkan Efeknya

useEffect juga memungkinkan kita untuk membersihkan sumber daya sebelum komponen dilepas. Ini mungkin diperlukan untuk mencegah kebocoran memori dan membuat aplikasi lebih efisien. Untuk melakukan ini, kami akan mengembalikan fungsi pembersihan di akhir hook.

 useEffect(() => { console.log('mounted') return () => console.log('unmounting... clean up here') })

Kait useEffect di atas akan mounted ke log saat komponen dipasang. Lepas... bersihkan di sini akan dicatat saat komponen dilepas. Ini bisa terjadi ketika komponen dihapus dari UI.

Proses pembersihan biasanya mengikuti formulir di bawah ini.

 useEffect(() => { //The effect we intend to make effect //We then return the clean up return () => the cleanup/unsubscription })

Meskipun Anda mungkin tidak menemukan begitu banyak kasus penggunaan untuk langganan useEffect , ini berguna saat menangani langganan dan pengatur waktu. Khususnya, ketika berhadapan dengan soket web, Anda mungkin perlu berhenti berlangganan dari jaringan untuk menghemat sumber daya dan meningkatkan kinerja saat komponen dilepas.

Mengambil Dan Mengambil Kembali Data Dengan useEffect

Salah satu kasus penggunaan paling umum dari kait useEffect adalah mengambil dan mengambil data dari API.

Untuk mengilustrasikannya, kita akan menggunakan data pengguna palsu yang saya buat dari JSONPlaceholder untuk mengambil data dengan kait useEffect .

 import { useEffect, useState } from "react"; import axios from "axios"; export default function App() { const [users, setUsers] = useState([]); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/users"; useEffect(() => { const fetchUsers = async () => { const { data } = await axios.get(endPoint); setUsers(data); }; fetchUsers(); }, []); return ( <div className="App"> {users.map((user) => ( <div> <h2>{user.name}</h2> <p>Occupation: {user.job}</p> <p>Sex: {user.sex}</p> </div> ))} </div> ); }

Dalam kode di atas, kami membuat status users menggunakan kait useState . Kemudian kami mengambil data dari API menggunakan Axios. Ini adalah proses asinkron, jadi kami menggunakan fungsi async/await, kami juga bisa menggunakan titik lalu sintaks. Karena kami mengambil daftar pengguna, kami hanya memetakannya untuk menampilkan data.

Perhatikan bahwa kami meneruskan parameter kosong ke hook. Ini memastikan bahwa itu dipanggil hanya sekali ketika komponen dipasang.

Kami juga dapat mengambil kembali data ketika beberapa kondisi berubah. Kami akan menunjukkan ini dalam kode di bawah ini.

 import { useEffect, useState } from "react"; import axios from "axios"; export default function App() { const [userIDs, setUserIDs] = useState([]); const [user, setUser] = useState({}); const [currentID, setCurrentID] = useState(1); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/userdata/users"; useEffect(() => { axios.get(endPoint).then(({ data }) => setUserIDs(data)); }, []); useEffect(() => { const fetchUserIDs = async () => { const { data } = await axios.get(`${endPoint}/${currentID}`}); setUser(data); }; fetchUserIDs(); }, [currentID]); const moveToNextUser = () => { setCurrentID((prevId) => (prevId < userIDs.length ? prevId + 1 : prevId)); }; const moveToPrevUser = () => { setCurrentID((prevId) => (prevId === 1 ? prevId : prevId - 1)); }; return ( <div className="App"> <div> <h2>{user.name}</h2> <p>Occupation: {user.job}</p> <p>Sex: {user.sex}</p> </div> <button onClick={moveToPrevUser}>Prev</button> <button onClick={moveToNextUser}>Next</button> </div> ); }

Di sini kami membuat dua kait useEffect . Yang pertama, kami menggunakan sintaks titik lalu untuk mendapatkan semua pengguna dari API kami. Ini diperlukan untuk menentukan jumlah pengguna.

Kami kemudian membuat kait useEffect lain untuk mendapatkan pengguna berdasarkan id . useEffect ini akan mengambil kembali data setiap kali id ​​berubah. Untuk memastikan ini, kami melewati id dalam daftar ketergantungan.

Selanjutnya, kami membuat fungsi untuk memperbarui nilai id kami setiap kali tombol diklik. Setelah nilai id berubah, useEffect akan berjalan lagi dan mengambil kembali data.

Jika kita mau, kita bahkan bisa membersihkan atau membatalkan token berbasis janji di Axios, kita bisa melakukannya dengan metode pembersihan yang dibahas di atas.

 useEffect(() => { const source = axios.CancelToken.source(); const fetchUsers = async () => { const { data } = await axios.get(`${endPoint}/${num}`, { cancelToken: source.token }); setUser(data); }; fetchUsers(); return () => source.cancel(); }, [num]);

Di sini, kami meneruskan token Axios sebagai parameter kedua ke axios.get . Saat komponen dilepas, kami kemudian membatalkan langganan dengan memanggil metode batal dari objek sumber.

useReducer

Kait useReducer adalah kait React yang sangat berguna yang melakukan hal serupa dengan kait useState . Menurut dokumentasi React, hook ini harus digunakan untuk menangani logika yang lebih kompleks daripada hook useState . Perlu dicatat bahwa kait useState diimplementasikan secara internal dengan kait useReducer.

Hook mengambil peredam sebagai argumen dan secara opsional dapat mengambil status awal dan fungsi init sebagai argumen.

 const [state, dispatch] = useReducer(reducer, initialState, init)

Di sini, init adalah fungsi dan digunakan kapan pun kita ingin membuat status awal dengan malas.

Mari kita lihat bagaimana menerapkan hook useReducer dengan membuat aplikasi to-do sederhana seperti yang ditunjukkan pada kotak pasir di bawah ini.

Contoh Todo

Pertama, kita harus membuat peredam untuk menahan status.

 export const ADD_TODO = "ADD_TODO"; export const REMOVE_TODO = "REMOVE_TODO"; export const COMPLETE_TODO = "COMPLETE_TODO"; const reducer = (state, action) => { switch (action.type) { case ADD_TODO: const newTodo = { id: action.id, text: action.text, completed: false }; return [...state, newTodo]; case REMOVE_TODO: return state.filter((todo) => todo.id !== action.id); case COMPLETE_TODO: const completeTodo = state.map((todo) => { if (todo.id === action.id) { return { ...todo, completed: !todo.completed }; } else { return todo; } }); return completeTodo; default: return state; } }; export default reducer;

Kami membuat tiga konstanta yang sesuai dengan jenis tindakan kami. Kita bisa saja menggunakan string secara langsung tetapi metode ini lebih disukai untuk menghindari kesalahan ketik.

Kemudian kami membuat fungsi peredam kami. Seperti di Redux , peredam harus mengambil status dan objek tindakan. Tapi tidak seperti Redux, kita tidak perlu menginisialisasi peredam kita di sini.

Selain itu, untuk banyak kasus penggunaan manajemen status, useReducer bersama dengan dispatch yang diekspos melalui konteks dapat memungkinkan aplikasi yang lebih besar untuk mengaktifkan tindakan, memperbarui state , dan mendengarkannya.

Kemudian kami menggunakan pernyataan switch untuk memeriksa jenis tindakan yang diteruskan oleh pengguna. Jika jenis tindakannya adalah ADD_TODO , kami ingin meneruskan tugas baru dan jika itu REMOVE_TODO , kami ingin memfilter tugas dan menghapus yang sesuai dengan id yang diteruskan oleh pengguna. Jika COMPLETE_TODO , kami ingin memetakan melalui to-dos dan beralih satu dengan id yang diteruskan oleh pengguna.

Berikut adalah file App.js tempat kami mengimplementasikan reducer .

 import { useReducer, useState } from "react"; import "./styles.css"; import reducer, { ADD_TODO, REMOVE_TODO, COMPLETE_TODO } from "./reducer"; export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = [ { id: id, text: "First Item", completed: false } ]; //We could also pass an empty array as the initial state //const initialState = [] const [state, dispatch] = useReducer(reducer, initialState); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); dispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; const removeTodo = (id) => { dispatch({ type: REMOVE_TODO, id }); }; const completeTodo = (id) => { dispatch({ type: COMPLETE_TODO, id }); }; return ( <div className="App"> <h1>Todo Example</h1> <form className="input" onSubmit={addTodoItem}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button disabled={text.length === 0} type="submit">+</button> </form> <div className="todos"> {state.map((todo) => ( <div key={todo.id} className="todoItem"> <p className={todo.completed && "strikethrough"}>{todo.text}</p> <span onClick={() => removeTodo(todo.id)}>✕</span> <span onClick={() => completeTodo(todo.id)}>✓</span> </div> ))} </div> </div> ); }

Di sini, kami membuat formulir yang berisi elemen input, untuk mengumpulkan input pengguna, dan tombol untuk memicu tindakan. Saat formulir dikirimkan, kami mengirimkan tindakan bertipe ADD_TODO , meneruskan id baru dan teks tugas. Kami membuat id baru dengan menambah nilai id sebelumnya dengan 1. Kami kemudian mengosongkan kotak teks input. Untuk menghapus dan menyelesaikan tugas, kami cukup mengirimkan tindakan yang sesuai. Ini telah diterapkan di peredam seperti yang ditunjukkan di atas.

Namun, keajaiban terjadi karena kami menggunakan kait useReducer . Kait ini menerima peredam dan status awal dan mengembalikan status dan fungsi pengiriman. Di sini, fungsi dispatch memiliki tujuan yang sama dengan fungsi setter untuk hook useState dan kita dapat menyebutnya apa pun yang kita inginkan alih-alih dispatch .

Untuk menampilkan item to-do, kita cukup memetakan daftar to-dos yang dikembalikan dalam objek state kita seperti yang ditunjukkan pada kode di atas.

Ini menunjukkan kekuatan hook useReducer . Kami juga dapat mencapai fungsi ini dengan kait useState tetapi seperti yang Anda lihat dari contoh di atas, kait useReducer membantu kami menjaga semuanya lebih rapi. useReducer sering bermanfaat ketika objek keadaan adalah struktur yang kompleks dan diperbarui dengan cara yang berbeda dibandingkan dengan penggantian nilai yang sederhana. Juga, setelah fungsi pembaruan ini menjadi lebih rumit, useReducer memudahkan untuk menyimpan semua kerumitan itu dalam fungsi peredam (yang merupakan fungsi JS murni) sehingga sangat mudah untuk menulis tes untuk fungsi peredam saja.

Kita juga bisa meneruskan argumen ketiga ke kait useReducer untuk membuat status awal dengan malas. Ini berarti bahwa kita dapat menghitung keadaan awal dalam fungsi init .

Misalnya, kita dapat membuat fungsi init sebagai berikut:

 const initFunc = () => [ { id: id, text: "First Item", completed: false } ]

dan kemudian meneruskannya ke kait useReducer kami.

 const [state, dispatch] = useReducer(reducer, initialState, initFunc)

Jika kita melakukan ini, initFunc akan menimpa initialState yang kita berikan dan initial state akan dihitung dengan malas.

useContext

React Context API menyediakan cara untuk berbagi status atau data di seluruh pohon komponen React. API telah tersedia di React, sebagai fitur eksperimental, untuk sementara waktu tetapi menjadi aman untuk digunakan di React 16.3.0. API memudahkan berbagi data antar komponen sekaligus menghilangkan pengeboran penyangga.

Meskipun Anda dapat menerapkan React Context ke seluruh aplikasi Anda, Anda juga dapat menerapkannya ke bagian aplikasi.

Untuk menggunakan hook, Anda harus terlebih dahulu membuat konteks menggunakan React.createContext dan konteks ini kemudian dapat diteruskan ke hook.

Untuk mendemonstrasikan penggunaan hook useContext , mari buat aplikasi sederhana yang akan meningkatkan ukuran font di seluruh aplikasi kita.

Mari kita buat konteks kita di file context.js .

 import { createContext } from "react"; //Here, we set the initial fontSize as 16. const fontSizeContext = createContext(16); export default fontSizeContext;

Di sini, kami membuat konteks dan meneruskan nilai awal 16 ke dalamnya, lalu mengekspor konteksnya. Selanjutnya, mari kita hubungkan konteks kita ke aplikasi kita.

 import FontSizeContext from "./context"; import { useState } from "react"; import PageOne from "./PageOne"; import PageTwo from "./PageTwo"; const App = () => { const [size, setSize] = useState(16); return ( <FontSizeContext.Provider value={size}> <PageOne /> <PageTwo /> <button onClick={() => setSize(size + 5)}>Increase font</button> <button onClick={() => setSize((prevSize) => Math.min(11, prevSize - 5)) } > Decrease font </button> </FontSizeContext.Provider> ); }; export default App;

Dalam kode di atas, kami membungkus seluruh pohon komponen kami dengan FontSizeContext.Provider dan meneruskan size ke prop nilainya. Di sini, size adalah status yang dibuat dengan kait useState . Ini memungkinkan kita untuk mengubah prop nilai setiap kali status size berubah. Dengan membungkus seluruh komponen dengan Provider , kita dapat mengakses konteks di mana saja dalam aplikasi kita.

Misalnya, kami mengakses konteks di <PageOne /> dan <PageTwo /> . Akibatnya, ukuran font akan meningkat di kedua komponen ini saat kami meningkatkannya dari file App.js Kami dapat menambah atau mengurangi ukuran font dari tombol seperti yang ditunjukkan di atas dan setelah kami melakukannya, ukuran font berubah di seluruh aplikasi.

 import { useContext } from "react"; import context from "./context"; const PageOne = () => { const size = useContext(context); return <p style={{ fontSize: `${size}px` }}>Content from the first page</p>; }; export default PageOne;

Di sini, kami mengakses konteks menggunakan kait useContext dari komponen PageOne kami. Kami kemudian menggunakan konteks ini untuk mengatur properti ukuran font kami. Prosedur serupa berlaku untuk file PageTwo.js .

Tema atau konfigurasi tingkat aplikasi tingkat tinggi lainnya adalah kandidat yang baik untuk konteks.

Menggunakan useContext Dan useReducer

Saat digunakan dengan kait useReducer , useContext memungkinkan kita membuat sistem manajemen status kita sendiri. Kami dapat membuat status global dan mengelolanya dengan mudah di aplikasi kami.

Mari kita tingkatkan aplikasi tugas kita menggunakan API konteks.

Seperti biasa, kita perlu membuat todoContext di file todoContext.js .

 import { createContext } from "react"; const initialState = []; export default createContext(initialState);

Di sini kita membuat konteksnya, meneruskan nilai awal dari array kosong. Kemudian kami mengekspor konteksnya.

Mari kita refactor file App.js kita dengan memisahkan daftar tugas dan item.

 import { useReducer, useState } from "react"; import "./styles.css"; import todoReducer, { ADD_TODO } from "./todoReducer"; import TodoContext from "./todoContext"; import TodoList from "./TodoList"; export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = []; const [todoState, todoDispatch] = useReducer(todoReducer, initialState); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); todoDispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; return ( <TodoContext.Provider value={[todoState, todoDispatch]}> <div className="app"> <h1>Todo Example</h1> <form className="input" onSubmit={addTodoItem}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button disabled={text.length === 0} type="submit"> + </button> </form> <TodoList /> </div> </TodoContext.Provider> ); }

Di sini, kami membungkus file App.js kami dengan TodoContext.Provider lalu kami meneruskan nilai pengembalian todoReducer kami ke sana. Ini membuat status peredam dan fungsi dispatch dapat diakses di seluruh aplikasi kita.

Kami kemudian memisahkan tampilan to-do menjadi komponen TodoList . Kami melakukan ini tanpa pengeboran penyangga, berkat API Konteks. Mari kita lihat file TodoList.js .

 import React, { useContext } from "react"; import TodoContext from "./todoContext"; import Todo from "./Todo"; const TodoList = () => { const [state] = useContext(TodoContext); return ( <div className="todos"> {state.map((todo) => ( <Todo key={todo.id} todo={todo} /> ))} </div> ); }; export default TodoList;

Menggunakan destrukturisasi array, kita dapat mengakses status (meninggalkan fungsi pengiriman) dari konteks menggunakan kait useContext . Kami kemudian dapat memetakan melalui negara bagian dan menampilkan item yang harus dilakukan. Kami masih mengekstrak ini dalam komponen Todo . Fungsi peta ES6+ mengharuskan kami untuk memberikan kunci unik dan karena kami memerlukan tugas khusus, kami juga meneruskannya bersama.

Mari kita lihat komponen Todo .

 import React, { useContext } from "react"; import TodoContext from "./todoContext"; import { REMOVE_TODO, COMPLETE_TODO } from "./todoReducer"; const Todo = ({ todo }) => { const [, dispatch] = useContext(TodoContext); const removeTodo = (id) => { dispatch({ type: REMOVE_TODO, id }); }; const completeTodo = (id) => { dispatch({ type: COMPLETE_TODO, id }); }; return ( <div className="todoItem"> <p className={todo.completed ? "strikethrough" : "nostrikes"}> {todo.text} </p> <span onClick={() => removeTodo(todo.id)}>✕</span> <span onClick={() => completeTodo(todo.id)}>✓</span> </div> ); }; export default Todo;

Sekali lagi menggunakan destrukturisasi array, kami mengakses fungsi pengiriman dari konteksnya. Ini memungkinkan kita untuk mendefinisikan fungsi completeTodo dan removeTodo seperti yang telah dibahas di bagian useReducer . Dengan prop todo yang diteruskan dari todoList.js kita dapat menampilkan item to-do. Kami juga dapat menandainya sebagai selesai dan menghapus tugas yang kami anggap cocok.

Dimungkinkan juga untuk menyarangkan lebih dari satu penyedia konteks di root aplikasi kita. Ini berarti bahwa kita dapat menggunakan lebih dari satu konteks untuk melakukan fungsi yang berbeda dalam suatu aplikasi.

Untuk mendemonstrasikan ini, mari tambahkan tema ke contoh tugas.

Inilah yang akan kita bangun.

Sekali lagi, kita harus membuat themeContext . Untuk melakukannya, buat file themeContext.js dan tambahkan kode berikut.

 import { createContext } from "react"; import colors from "./colors"; export default createContext(colors.light);

Di sini, kami membuat konteks dan meneruskan colors.light sebagai nilai awal. Mari kita definisikan warna dengan properti ini di file colors.js .

 const colors = { light: { backgroundColor: "#fff", color: "#000" }, dark: { backgroundColor: "#000", color: "#fff" } }; export default colors;

Pada kode di atas, kita membuat objek colors yang berisi properti terang dan gelap. Setiap properti memiliki backgroundColor dan objek color .

Selanjutnya, kita membuat themeReducer untuk menangani status tema.

 import Colors from "./colors"; export const LIGHT = "LIGHT"; export const DARK = "DARK"; const themeReducer = (state, action) => { switch (action.type) { case LIGHT: return { ...Colors.light }; case DARK: return { ...Colors.dark }; default: return state; } }; export default themeReducer;

Seperti semua reduksi, themeReducer mengambil status dan tindakan. Kemudian menggunakan pernyataan switch untuk menentukan tindakan saat ini. Jika bertipe LIGHT , kita cukup menetapkan props Colors.light dan jika bertipe DARK , kita menampilkan props Colors.dark . Kami dapat dengan mudah melakukan ini dengan kait useState tetapi kami memilih useReducer untuk mengarahkan poin ke rumah.

Setelah menyiapkan themeReducer , kita dapat mengintegrasikannya ke dalam file App.js kita.

 import { useReducer, useState, useCallback } from "react"; import "./styles.css"; import todoReducer, { ADD_TODO } from "./todoReducer"; import TodoContext from "./todoContext"; import ThemeContext from "./themeContext"; import TodoList from "./TodoList"; import themeReducer, { DARK, LIGHT } from "./themeReducer"; import Colors from "./colors"; import ThemeToggler from "./ThemeToggler"; const themeSetter = useCallback( theme => themeDispatch({type: theme}, [themeDispatch]); export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = []; const [todoState, todoDispatch] = useReducer(todoReducer, initialState); const [themeState, themeDispatch] = useReducer(themeReducer, Colors.light); const themeSetter = useCallback( (theme) => { themeDispatch({ type: theme }); }, [themeDispatch] ); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); todoDispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; return ( <TodoContext.Provider value={[todoState, todoDispatch]}> <ThemeContext.Provider value={[ themeState, themeSetter ]} > <div className="app" style={{ ...themeState }}> <ThemeToggler /> <h1>Todo Example</h1> <form className="input" onSubmit={addTodoItem}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button disabled={text.length === 0} type="submit"> + </button> </form> <TodoList /> </div> </ThemeContext.Provider> </TodoContext.Provider> ); }

Pada kode di atas, kami menambahkan beberapa hal ke aplikasi to-do yang sudah ada. Kami mulai dengan mengimpor ThemeContext , themeReducer , ThemeToggler , dan Colors . We created a reducer using the useReducer hook, passing the themeReducer and an initial value of Colors.light to it. This returned the themeState and themeDispatch to us.

We then nested our component with the provider function from the ThemeContext , passing the themeState and the dispatch functions to it. We also added theme styles to it by spreading out the themeStates . This works because the colors object already defined properties similar to what the JSX styles will accept.

However, the actual theme toggling happens in the ThemeToggler component. Mari kita lihat itu.

 import ThemeContext from "./themeContext"; import { useContext, useState } from "react"; import { DARK, LIGHT } from "./themeReducer"; const ThemeToggler = () => { const [showLight, setShowLight] = useState(true); const [themeState, themeSetter] = useContext(ThemeContext); const dispatchDarkTheme = () => themeSetter(DARK); const dispatchLightTheme = () => themeSetter(LIGHT); const toggleTheme = () => { showLight ? dispatchDarkTheme() : dispatchLightTheme(); setShowLight(!showLight); }; console.log(themeState); return ( <div> <button onClick={toggleTheme}> {showLight ? "Change to Dark Theme" : "Change to Light Theme"} </button> </div> ); }; export default ThemeToggler;

In this component, we used the useContext hook to retrieve the values we passed to the ThemeContext.Provider from our App.js file. As shown above, these values include the ThemeState , dispatch function for the light theme, and dispatch function for the dark theme. Thereafter, we simply called the dispatch functions to toggle the themes. We also created a state showLight to determine the current theme. This allows us to easily change the button text depending on the current theme.

The useMemo Hook

The useMemo hook is designed to memoize expensive computations. Memoization simply means caching. It caches the computation result with respect to the dependency values so that when the same values are passed, useMemo will just spit out the already computed value without recomputing it again. This can significantly improve performance when done correctly.

The hook can be used as follows:

 const memoizedResult = useMemo(() => expensiveComputation(a, b), [a, b])

Let's consider three cases of the useMemo hook.

  1. When the dependency values, a and b remain the same.
    The useMemo hook will return the already computed memoized value without recomputation.
  2. When the dependency values, a and b change.
    The hook will recompute the value.
  3. When no dependency value is passed.
    The hook will recompute the value.

Let's take a look at an example to demonstrate this concept.

In the example below, we'll be computing the PAYE and Income after PAYE of a company's employees with fake data from JSONPlaceholder.

The calculation will be based on the personal income tax calculation procedure for Nigeria providers by PricewaterhouseCoopers available here.

This is shown in the sandbox below.

First, we queried the API to get the employees' data. We also get data for each employee (with respect to their employee id).

const [employee, setEmployee] = useState({}); const [employees, setEmployees] = useState([]); const [num, setNum] = useState(1); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees"; useEffect(() => { const getEmployee = async () => { const { data } = await axios.get(`${endPoint}/${num}`); setEmployee(data); }; getEmployee(); }, [num]); useEffect(() => { axios.get(endPoint).then(({ data }) => setEmployees(data)); }, [num]);

Kami menggunakan axios dan metode async/await di useEffect pertama dan kemudian titik kemudian sintaks di yang kedua. Kedua pendekatan ini bekerja dengan cara yang sama.

Selanjutnya, dengan menggunakan data karyawan yang kita dapatkan dari atas, mari kita hitung variabel reliefnya:

 const taxVariablesCompute = useMemo(() => { const { income, noOfChildren, noOfDependentRelatives } = employee; //supposedly complex calculation //tax relief computations for relief Allowance, children relief, // relatives relief and pension relief const reliefs = reliefAllowance1 + reliefAllowance2 + childrenRelief + relativesRelief + pensionRelief; return reliefs; }, [employee]);

Ini adalah perhitungan yang cukup rumit dan jadi kami harus membungkusnya dalam kait useMemo untuk memoize atau mengoptimalkannya. Memoisasinya dengan cara ini akan memastikan bahwa perhitungan tidak akan dihitung ulang jika kami mencoba mengakses karyawan yang sama lagi.

Selanjutnya, dengan menggunakan nilai keringanan pajak yang diperoleh di atas, kami ingin menghitung PAYE dan pendapatan setelah PAYE.

 const taxCalculation = useMemo(() => { const { income } = employee; let taxableIncome = income - taxVariablesCompute; let PAYE = 0; //supposedly complex calculation //computation to compute the PAYE based on the taxable income and tax endpoints const netIncome = income - PAYE; return { PAYE, netIncome }; }, [employee, taxVariablesCompute]);

Kami melakukan perhitungan pajak (perhitungan yang cukup rumit) menggunakan variabel pajak yang dihitung di atas dan kemudian memonya dengan hook useMemo .

Kode lengkap tersedia di sini.

Ini mengikuti prosedur perhitungan pajak yang diberikan di sini. Kami pertama-tama menghitung keringanan pajak dengan mempertimbangkan pendapatan, jumlah anak, dan jumlah kerabat yang menjadi tanggungan. Kemudian, kami mengalikan penghasilan kena pajak dengan tarif PIT secara bertahap. Sementara perhitungan yang dimaksud tidak sepenuhnya diperlukan untuk tutorial ini, ini disediakan untuk menunjukkan kepada kita mengapa useMemo mungkin diperlukan. Ini juga merupakan perhitungan yang cukup rumit sehingga kita mungkin perlu mengingatnya dengan useMemo seperti yang ditunjukkan di atas.

Setelah menghitung nilai, kami hanya menampilkan hasilnya.

Perhatikan hal berikut tentang kait useMemo .

  • useMemo harus digunakan hanya jika diperlukan untuk mengoptimalkan komputasi. Dengan kata lain, ketika penghitungan ulang itu mahal.
  • Disarankan untuk menulis perhitungan terlebih dahulu tanpa menghafal dan hanya menghafalnya jika menyebabkan masalah kinerja.
  • Penggunaan hook useMemo yang tidak perlu dan tidak relevan bahkan dapat menambah masalah kinerja.
  • Terkadang, terlalu banyak memoisasi juga dapat menyebabkan masalah kinerja.

useCallback yang digunakan

useCallback melayani tujuan yang sama seperti useMemo tetapi mengembalikan panggilan balik yang di-memo alih-alih nilai yang di-memo. Dengan kata lain, useCallback sama dengan melewatkan useMemo tanpa pemanggilan fungsi.

Misalnya, perhatikan kode berikut di bawah ini.

 import React, {useCallback, useMemo} from 'react' const MemoizationExample = () => { const a = 5 const b = 7 const memoResult = useMemo(() => a + b, [a, b]) const callbackResult = useCallback(a + b, [a, b]) console.log(memoResult) console.log(callbackResult) return( <div> ... </div> ) } export default MemoizationExample

Pada contoh di atas, baik memoResult dan callbackResult akan memberikan nilai yang sama yaitu 12 . Di sini, useCallback akan mengembalikan nilai memo. Namun, kami juga dapat membuatnya mengembalikan panggilan balik memo dengan meneruskannya sebagai fungsi.

useCallback di bawah ini akan mengembalikan panggilan balik memo.

 ... const callbackResult = useCallback(() => a + b, [a, b]) ...

Kami kemudian dapat memicu panggilan balik ketika suatu tindakan dilakukan atau di kait useEffect .

 import {useCallback, useEffect} from 'react' const memoizationExample = () => { const a = 5 const b = 7 const callbackResult = useCallback(() => a + b, [a, b]) useEffect(() => { const callback = callbackResult() console.log(callback) }) return ( <div> <button onClick= {() => console.log(callbackResult())}> Trigger Callback </button> </div> ) } export default memoizationExample

Dalam kode di atas, kami mendefinisikan fungsi panggilan balik menggunakan kait useCallback . Kami kemudian memanggil panggilan balik di kait useEffect saat komponen dipasang dan juga saat tombol diklik.

Baik useEffect maupun klik tombol menghasilkan hasil yang sama.

Perhatikan bahwa konsep, tindakan, dan larangan yang berlaku untuk kait useMemo juga berlaku untuk kait useCallback . Kita dapat membuat ulang contoh useMemo dengan useCallback .

useRef

useRef mengembalikan objek yang dapat bertahan dalam aplikasi. Hook hanya memiliki satu properti, current , dan kita dapat dengan mudah memberikan argumen padanya.

Ini melayani tujuan yang sama dengan createRef yang digunakan dalam komponen berbasis kelas. Kita dapat membuat referensi dengan hook ini sebagai berikut:

 const newRef = useRef('')

Di sini kami membuat referensi baru yang disebut newRef dan meneruskan string kosong ke sana.

Kait ini digunakan terutama untuk dua tujuan:

  1. Mengakses atau memanipulasi DOM, dan
  2. Menyimpan status yang dapat diubah — ini berguna saat kita tidak ingin komponen dirender saat nilai berubah.

Memanipulasi DOM

Saat diteruskan ke elemen DOM, objek ref menunjuk ke elemen itu dan dapat digunakan untuk mengakses atribut dan properti DOM-nya.

Berikut adalah contoh yang sangat sederhana untuk menunjukkan konsep ini.

 import React, {useRef, useEffect} from 'react' const RefExample = () => { const headingRef = useRef('') console.log(headingRef) return( <div> <h1 className='topheading' ref={headingRef}>This is a h1 element</h1> </div> ) } export default RefExample

Pada contoh di atas, kita mendefinisikan headingRef menggunakan hook useRef yang meneruskan string kosong. Kami kemudian mengatur ref di tag h1 dengan meneruskan ref = {headingRef} . Dengan mengatur ref ini, kami telah meminta headingRef untuk menunjuk ke elemen h1 kami. Ini berarti bahwa kita dapat mengakses properti elemen h1 kita dari referensi.

Untuk melihat ini, jika kita memeriksa nilai console.log(headingRef) , kita akan mendapatkan {current: HTMLHeadingElement} atau {current: h1} dan kita dapat menilai semua properti atau atribut elemen. Hal serupa berlaku untuk elemen HTML lainnya.

Misalnya, kita bisa membuat teks miring saat komponen dipasang.

 useEffect(() => { headingRef.current.style.font; }, []);

Kita bahkan dapat mengubah teks menjadi sesuatu yang lain.

 ... headingRef.current.innerHTML = "A Changed H1 Element"; ...

Kami bahkan dapat mengubah warna latar belakang wadah induk juga.

 ... headingRef.current.parentNode.style.backgroundColor = "red"; ...

Segala jenis manipulasi DOM dapat dilakukan di sini. Perhatikan bahwa headingRef.current dapat dibaca dengan cara yang sama seperti document.querySelector('.topheading') .

Salah satu kasus penggunaan yang menarik dari kait useRef dalam memanipulasi elemen DOM adalah memfokuskan kursor pada elemen input. Mari kita cepat menjalankannya.

 import {useRef, useEffect} from 'react' const inputRefExample = () => { const inputRef = useRef(null) useEffect(() => { inputRef.current.focus() }, []) return( <div> <input ref={inputRef} /> <button onClick = {() => inputRef.current.focus()}>Focus on Input </button> </div> ) } export default inputRefExample

Dalam kode di atas, kita membuat inputRef menggunakan kait useRef dan kemudian memintanya untuk menunjuk ke elemen input. Kami kemudian membuat kursor fokus pada referensi masukan saat komponen dimuat dan saat tombol diklik menggunakan inputRef.current.focus() . Ini dimungkinkan karena focus() adalah atribut dari elemen input sehingga ref akan dapat menilai metode.

Referensi yang dibuat dalam komponen induk dapat dinilai pada komponen turunan dengan meneruskannya menggunakan React.forwardRef() . Mari kita lihat itu.

Pertama-tama mari kita buat komponen lain NewInput.js dan tambahkan kode berikut ke dalamnya.

 import { useRef, forwardRef } from "react"; const NewInput = forwardRef((props, ref) => { return <input placeholder={props.val} ref={ref} />; }); export default NewInput;

Komponen ini menerima props dan ref . Kami meneruskan referensi ke prop prop dan props.val ke prop placeholder-nya. Komponen React Reguler tidak mengambil atribut ref . Atribut ini hanya tersedia ketika kita membungkusnya dengan React.forwardRef seperti yang ditunjukkan di atas.

Kami kemudian dapat dengan mudah memanggil ini di komponen induk.

 ... <NewInput val="Just an example" ref={inputRef} /> ...

Menyimpan Negara yang Bisa Berubah

Referensi tidak hanya digunakan untuk memanipulasi elemen DOM, tetapi juga dapat digunakan untuk menyimpan nilai yang dapat diubah tanpa merender ulang seluruh komponen.

Contoh berikut akan mendeteksi berapa kali tombol diklik tanpa merender ulang komponen.

 import { useRef } from "react"; export default function App() { const countRef = useRef(0); const increment = () => { countRef.current++; console.log(countRef); }; return ( <div className="App"> <button onClick={increment}>Increment </button> </div> ); }

Dalam kode di atas, kami menambah countRef ketika tombol diklik dan kemudian mencatatnya ke konsol. Meskipun nilainya bertambah seperti yang ditunjukkan di konsol, kami tidak akan dapat melihat perubahan apa pun jika kami mencoba menilainya langsung di komponen kami. Itu hanya akan diperbarui di komponen saat dirender ulang.

Perhatikan bahwa sementara useState tidak sinkron, useRef sinkron. Dengan kata lain, nilai tersedia segera setelah diperbarui.

useLayoutEffect

Seperti kait useEffect , useLayoutEffect dipanggil setelah komponen dipasang dan dirender. Hook ini diaktifkan setelah mutasi DOM dan melakukannya secara serempak. Selain dipanggil secara sinkron setelah mutasi DOM, useLayoutEffect melakukan hal yang sama seperti useEffect .

useLayoutEffect hanya boleh digunakan untuk melakukan mutasi DOM atau pengukuran terkait DOM, jika tidak, Anda harus menggunakan hook useEffect . Menggunakan kait useEffect untuk fungsi mutasi DOM dapat menyebabkan beberapa masalah kinerja seperti kedipan tetapi useLayoutEffect menanganinya dengan sempurna saat dijalankan setelah mutasi terjadi.

Mari kita lihat beberapa contoh untuk menunjukkan konsep ini.

  1. Kami akan mendapatkan lebar dan tinggi jendela saat mengubah ukuran.
 import {useState, useLayoutEffect} from 'react' const ResizeExample = () =>{ const [windowSize, setWindowSize] = useState({width: 0, height: 0}) useLayoutEffect(() => { const resizeWindow = () => setWindowSize({ width: window.innerWidth, height: window.innerHeight }) window.addEventListener('resize', resizeWindow) return () => window.removeEventListener('resize', resizeWindow) }, []) return ( <div> <p>width: {windowSize.width}</p> <p>height: {windowSize.height}</p> </div> ) } export default ResizeExample

Dalam kode di atas, kami membuat status windowSize dengan properti lebar dan tinggi. Kemudian kita mengatur status ke lebar dan tinggi jendela saat ini masing-masing ketika jendela diubah ukurannya. Kami juga membersihkan kode saat dilepas. Proses pembersihan sangat penting dalam useLayoutEffect untuk membersihkan manipulasi DOM dan meningkatkan efisiensi.

  1. Mari kita mengaburkan teks dengan useLayoutEffect .
 import { useRef, useState, useLayoutEffect } from "react"; export default function App() { const paragraphRef = useRef(""); useLayoutEffect(() => { const { current } = paragraphRef; const blurredEffect = () => { current.style.color = "transparent"; current.style.textShadow = "0 0 5px rgba(0,0,0,0.5)"; }; current.addEventListener("click", blurredEffect); return () => current.removeEventListener("click", blurredEffect); }, []); return ( <div className="App"> <p ref={paragraphRef}>This is the text to blur</p> </div> ); }

Kami menggunakan useRef dan useLayoutEffect bersama-sama dalam kode di atas. Kami pertama kali membuat ref, paragraphRef untuk menunjuk ke paragraf kami. Kemudian kami membuat event listener on-click untuk memantau saat paragraf diklik dan kemudian mengaburkannya menggunakan properti gaya yang kami tetapkan. Terakhir, kami membersihkan pendengar acara menggunakan removeEventListener .

useDispatch Dan useSelector Hooks

useDispatch adalah pengait Redux untuk mengirim (memicu) tindakan dalam suatu aplikasi. Dibutuhkan objek tindakan sebagai argumen dan memanggil tindakan. useDispatch adalah kesetaraan hook dengan mapDispatchToProps .

Di sisi lain, useSelector adalah pengait Redux untuk menilai status Redux. Dibutuhkan fungsi untuk memilih peredam Redux yang tepat dari toko dan kemudian mengembalikan status yang sesuai.

Setelah toko Redux kami terhubung ke aplikasi React melalui penyedia Redux, kami dapat menjalankan tindakan dengan useDispatch dan mengakses status dengan useSelector . Setiap tindakan dan status Redux dapat dinilai dengan dua kait ini.

Perhatikan bahwa status ini dikirimkan dengan React Redux (paket yang memudahkan penilaian penyimpanan Redux dalam aplikasi React). Mereka tidak tersedia di perpustakaan inti Redux.

Kait ini sangat mudah digunakan. Pertama, kita harus mendeklarasikan fungsi pengiriman dan kemudian memicunya.

 import {useDispatch, useSelector} from 'react-redux' import {useEffect} from 'react' const myaction from '...' const ReduxHooksExample = () =>{ const dispatch = useDispatch() useEffect(() => { dispatch(myaction()); //alternatively, we can do this dispatch({type: 'MY_ACTION_TYPE'}) }, []) const mystate = useSelector(state => state.myReducerstate) return( ... ) } export default ReduxHooksExample

Dalam kode di atas, kami mengimpor useDispatch dan useSelector dari react-redux . Kemudian, di kait useEffect , kami mengirimkan tindakan. Kita bisa mendefinisikan tindakan di file lain dan kemudian memanggilnya di sini atau kita bisa mendefinisikannya secara langsung seperti yang ditunjukkan dalam panggilan useEffect .

Setelah kami mengirimkan tindakan, status kami akan tersedia. Kami kemudian dapat mengambil status menggunakan kait useSelector seperti yang ditunjukkan. Status dapat digunakan dengan cara yang sama seperti kita menggunakan status dari kait useState .

Mari kita lihat contoh untuk mendemonstrasikan kedua kait ini.

Untuk mendemonstrasikan konsep ini, kita harus membuat toko Redux, peredam, dan tindakan. Untuk menyederhanakan hal-hal di sini, kami akan menggunakan perpustakaan Redux Toolkit dengan database palsu kami dari JSONPlaceholder.

Kita perlu menginstal paket-paket berikut untuk memulai. Jalankan perintah bash berikut.

 npm i redux @reduxjs/toolkit react-redux axios

Pertama, mari buat employeesSlice.js untuk menangani peredam dan aksi untuk API karyawan kita.

 import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import axios from "axios"; const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees"; export const fetchEmployees = createAsyncThunk("employees/fetchAll", async () => { const { data } = await axios.get(endPoint); return data; }); const employeesSlice = createSlice({ name: "employees", initialState: { employees: [], loading: false, error: "" }, reducers: {}, extraReducers: { [fetchEmployees.pending]: (state, action) => { state.status = "loading"; }, [fetchEmployees.fulfilled]: (state, action) => { state.status = "success"; state.employees = action.payload; }, [fetchEmployees.rejected]: (state, action) => { state.status = "error"; state.error = action.error.message; } } }); export default employeesSlice.reducer;

Ini adalah pengaturan standar untuk toolkit Redux. Kami menggunakan createAsyncThunk untuk mengakses middleware Thunk untuk melakukan tindakan asinkron. Ini memungkinkan kami mengambil daftar karyawan dari API. Kami kemudian membuat employeesSlice dan mengembalikan, "memuat", "kesalahan", dan data karyawan tergantung pada jenis tindakan.

Toolkit Redux juga memudahkan pengaturan toko. Berikut adalah toko.

 import { configureStore } from "@reduxjs/toolkit"; import { combineReducers } from "redux"; import employeesReducer from "./employeesSlice"; const reducer = combineReducers({ employees: employeesReducer }); export default configureStore({ reducer });;

Di sini, kami menggunakan combineReducers untuk menggabungkan reduksi dan fungsi configureStore yang disediakan oleh toolkit Redux untuk menyiapkan penyimpanan.

Mari kita lanjutkan untuk menggunakan ini di aplikasi kita.

Pertama, kita perlu menghubungkan Redux ke aplikasi React kita. Idealnya, ini harus dilakukan di root aplikasi kita. Saya suka melakukannya di file index.js .

 import React, { StrictMode } from "react"; import ReactDOM from "react-dom"; import store from "./redux/store"; import { Provider } from "react-redux"; import App from "./App"; const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <StrictMode> <App /> </StrictMode> </Provider>, rootElement );

Di sini, saya telah mengimpor toko yang saya buat di atas dan juga Provider dari react-redux .

Kemudian, saya membungkus seluruh aplikasi dengan fungsi Provider , meneruskan toko ke sana. Ini membuat toko dapat diakses di seluruh aplikasi kami.

Kami kemudian dapat melanjutkan untuk menggunakan kait useDispatch dan useSelector untuk mengambil data.

Mari kita lakukan ini di file App.js kita.

 import { useDispatch, useSelector } from "react-redux"; import { fetchEmployees } from "./redux/employeesSlice"; import { useEffect } from "react"; export default function App() { const dispatch = useDispatch(); useEffect(() => { dispatch(fetchEmployees()); }, [dispatch]); const employeesState = useSelector((state) => state.employees); const { employees, loading, error } = employeesState; return ( <div className="App"> {loading ? ( "Loading..." ) : error ? ( <div>{error}</div> ) : ( <> <h1>List of Employees</h1> {employees.map((employee) => ( <div key={employee.id}> <h3>{`${employee.firstName} ${employee.lastName}`}</h3> </div> ))} </> )} </div> ); }

Dalam kode di atas, kami menggunakan kait useDispatch untuk menjalankan tindakan fetchEmployees yang dibuat di file employeesSlice.js . Ini membuat status karyawan tersedia di aplikasi kami. Kemudian, kami menggunakan kait useSelector untuk mendapatkan status. Setelah itu, kami menampilkan hasilnya dengan pemetaan melalui employees .

useHistory Sejarah Penggunaan

Navigasi sangat penting dalam aplikasi React. Meskipun Anda dapat mencapai ini dalam beberapa cara, React Router menyediakan cara yang sederhana, efisien, dan populer untuk mencapai perutean dinamis dalam aplikasi React. Selanjutnya, React Router menyediakan beberapa kait untuk menilai keadaan router dan melakukan navigasi pada browser tetapi untuk menggunakannya, Anda harus terlebih dahulu mengatur aplikasi Anda dengan benar.

Untuk menggunakan hook React Router, pertama-tama kita harus membungkus aplikasi kita dengan BrowserRouter . Kami kemudian dapat membuat sarang rute dengan Switch dan Route .

Tapi pertama-tama, kita harus menginstal paket dengan menjalankan perintah berikut.

 npm install react-router-dom

Kemudian, kita perlu mengatur aplikasi kita sebagai berikut. Saya suka melakukan ini di file App.js saya.

 import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; import Employees from "./components/Employees"; export default function App() { return ( <div className="App"> <Router> <Switch> <Route path='/'> <Employees /> </Route> ... </Switch> </Router> </div> ); }

Kita dapat memiliki Rute sebanyak mungkin tergantung pada jumlah komponen yang ingin kita render. Di sini, kami hanya merender komponen Employees . Atribut path memberi tahu React Router DOM jalur komponen dan dapat dinilai dengan string kueri atau berbagai metode lainnya.

Urutan penting di sini. Rute root harus ditempatkan di bawah rute anak dan seterusnya. Untuk mengganti urutan ini, Anda harus menyertakan kata kunci yang exact pada rute root.

 <Route path='/' exact > <Employees /> </Route>

Sekarang setelah kita mengatur router, kita kemudian dapat menggunakan hook useHistory dan hook Router React lainnya di aplikasi kita.

Untuk menggunakan hook useHistory , pertama-tama kita harus mendeklarasikannya sebagai berikut.

 import {useHistory} from 'history' import {useHistory} from 'react-router-dom' const Employees = () =>{ const history = useHistory() ... }

Jika kita mencatat riwayat ke konsol, kita akan melihat beberapa properti yang terkait dengannya. Ini termasuk block , createHref , go , goBack , goForward , length , listen , location , push , replace . Meskipun semua properti ini berguna, kemungkinan besar Anda akan menggunakan history.push dan history.replace lebih sering daripada properti lainnya.

Mari kita gunakan properti ini untuk berpindah dari satu halaman ke halaman lainnya.

Dengan asumsi kami ingin mengambil data tentang karyawan tertentu ketika kami mengklik nama mereka. Kita dapat menggunakan kait useHistory untuk menavigasi ke halaman baru tempat informasi karyawan akan ditampilkan.

 function moveToPage = (id) =>{ history.push(`/employees/${id}`) }

Kita dapat mengimplementasikan ini di file Employee.js kita dengan menambahkan yang berikut ini.

 import { useEffect } from "react"; import { Link, useHistory, useLocation } from "react-router-dom"; export default function Employees() { const history = useHistory(); function pushToPage = (id) => { history.push(`/employees/${id}`) } ... return ( <div> ... <h1>List of Employees</h1> {employees.map((employee) => ( <div key={employee.id}> <span>{`${employee.firstName} ${employee.lastName} `}</span> <button onClick={pushToPage(employee.id)}> » </button> </div> ))} </div> ); }

Dalam fungsi pushToPage , kami menggunakan history dari kait useHistory untuk menavigasi ke halaman karyawan dan meneruskan id karyawan di sampingnya.

useLocation Lokasi Penggunaan

Hook ini juga disertakan dengan React Router DOM. Ini adalah kait yang sangat populer yang digunakan untuk bekerja dengan parameter string kueri. Kait ini mirip dengan window.location di browser.

 import {useLocation} from 'react' const LocationExample = () =>{ const location = useLocation() return ( ... ) } export default LocationExample

Kait useLocation mengembalikan pathname , parameter search , hash dan state . Parameter yang paling umum digunakan termasuk pathname dan search tetapi Anda juga bisa menggunakan hash , dan state banyak hal di aplikasi Anda.

Properti location pathname akan mengembalikan jalur yang kita atur dalam pengaturan Route kita. Sementara search akan mengembalikan parameter pencarian kueri jika ada. Misalnya, jika kita meneruskan 'http://mywebsite.com/employee/?id=1' ke kueri kita, nama pathname adalah /employee dan search adalah ?id=1 .

Kami kemudian dapat mengambil berbagai parameter pencarian menggunakan paket seperti string kueri atau dengan mengkodekannya.

useParams

Jika kita menyiapkan Route dengan parameter URL di atribut path-nya, kita dapat menilai parameter tersebut sebagai pasangan kunci/nilai dengan hook useParams .

Sebagai contoh, mari kita asumsikan bahwa kita memiliki Route berikut.

 <Route path='/employees/:id' > <Employees /> </Route>

Route akan mengharapkan id dinamis sebagai ganti :id .

Dengan kait useParams , kita dapat menilai id yang diteruskan oleh pengguna, jika ada.

Misalnya, dengan asumsi pengguna melewati fungsi berikut dengan history.push ,

 function goToPage = () => { history.push(`/employee/3`) }

Kita dapat menggunakan kait useParams untuk mengakses parameter URL ini sebagai berikut.

 import {useParams} from 'react-router-dom' const ParamsExample = () =>{ const params = useParams() console.log(params) return( <div> ... </div> ) } export default ParamsExample

Jika kita mencatat params ke konsol, kita akan mendapatkan objek berikut {id: "3"} .

useRouteMatch

Kait ini menyediakan akses ke objek yang cocok. Ini mengembalikan kecocokan terdekat dengan komponen jika tidak ada argumen yang diberikan padanya.

Objek kecocokan mengembalikan beberapa parameter termasuk path (sama dengan jalur yang ditentukan di Route), URL , objek params , dan isExact .

Misalnya, kita dapat menggunakan useRouteMatch untuk mengembalikan komponen berdasarkan rute.

 import { useRouteMatch } from "react-router-dom"; import Employees from "..."; import Admin from "..." const CustomRoute = () => { const match = useRouteMatch("/employees/:id"); return match ? ( <Employee /> ) : ( <Admin /> ); }; export default CustomRoute;

Dalam kode di atas, kami menetapkan jalur rute dengan useRouteMatch dan kemudian merender komponen <Employee /> atau <Admin /> tergantung pada rute yang dipilih oleh pengguna.

Agar ini berfungsi, kita masih perlu menambahkan rute ke file App.js kita.

 ... <Route> <CustomRoute /> </Route> ...

Membangun Kait Kustom

Menurut dokumentasi React, membangun custom hook memungkinkan kita untuk mengekstrak logika menjadi fungsi yang dapat digunakan kembali. Namun, Anda perlu memastikan bahwa semua aturan yang berlaku untuk kait React berlaku untuk kait khusus Anda. Periksa aturan React hook di bagian atas tutorial ini dan pastikan custom hook Anda sesuai dengan masing-masing aturan tersebut.

Kait khusus memungkinkan kita untuk menulis fungsi sekali dan menggunakannya kembali kapan pun dibutuhkan dan karenanya mematuhi prinsip KERING.

Misalnya, kita dapat membuat kait khusus untuk mendapatkan posisi gulir di halaman kita sebagai berikut.

 import { useLayoutEffect, useState } from "react"; export const useScrollPos = () => { const [scrollPos, setScrollPos] = useState({ x: 0, y: 0 }); useLayoutEffect(() => { const getScrollPos = () => setScrollPos({ x: window.pageXOffset, y: window.pageYOffset }); window.addEventListener("scroll", getScrollPos); return () => window.removeEventListener("scroll", getScrollPos); }, []); return scrollPos; };

Di sini, kami mendefinisikan kait khusus untuk menentukan posisi gulir pada halaman. Untuk mencapai ini, pertama-tama kami membuat status, scrollPos , untuk menyimpan posisi gulir. Karena ini akan memodifikasi DOM, kita perlu menggunakan useLayoutEffect alih-alih useEffect . Kami menambahkan pendengar acara gulir untuk menangkap posisi gulir x dan y dan kemudian membersihkan pendengar acara. Akhirnya, kami kembali ke posisi gulir.

Kami dapat menggunakan kait khusus ini di mana saja di aplikasi kami dengan memanggilnya dan menggunakannya seperti halnya kami menggunakan status lainnya.

 import {useScrollPos} from './Scroll' const App = () =>{ const scrollPos = useScrollPos() console.log(scrollPos.x, scrollPos.y) return ( ... ) } export default App

Di sini, kami mengimpor kait khusus useScrollPos yang kami buat di atas. Kemudian kami menginisialisasinya dan kemudian mencatat nilainya ke konsol kami. Jika kita menggulir pada halaman, pengait akan menunjukkan kepada kita posisi gulir di setiap langkah gulir.

Kami dapat membuat kait khusus untuk melakukan apa saja yang dapat kami bayangkan di aplikasi kami. Seperti yang Anda lihat, kita hanya perlu menggunakan kait React bawaan untuk menjalankan beberapa fungsi. Kami juga dapat menggunakan perpustakaan pihak ketiga untuk membuat kait khusus tetapi jika kami melakukannya, kami harus menginstal perpustakaan itu untuk dapat menggunakan kait.

Kesimpulan

Dalam tutorial ini, kami melihat beberapa hook React berguna yang akan Anda gunakan di sebagian besar aplikasi Anda. Kami memeriksa apa yang mereka hadirkan dan bagaimana menggunakannya dalam aplikasi Anda. Kami juga melihat beberapa contoh kode untuk membantu Anda memahami kait ini dan menerapkannya ke aplikasi Anda.

Saya mendorong Anda untuk mencoba kait ini di aplikasi Anda sendiri untuk lebih memahaminya.

Sumber Daya Dari The React Docs

  • FAQ Kait
  • Perangkat Redux
  • Menggunakan Kait Negara
  • Menggunakan Kait Efek
  • Referensi API Kait
  • React Redux Hooks
  • Bereaksi Kait Router