React Hooks Berguna Yang Dapat Anda Gunakan Dalam Proyek Anda
Diterbitkan: 2022-03-10Hooks 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.
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.
- Konvensi penamaan hook harus dimulai dengan awalan
use
. Jadi, kita dapat memilikiuseState
,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. - Kait harus dipanggil di tingkat atas komponen, sebelum pernyataan kembali. Mereka tidak dapat dipanggil di dalam pernyataan bersyarat, loop, atau fungsi bersarang.
- 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.
- Saat dipasang — setelah komponen dirender.
- 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.
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.
- When the dependency values, a and b remain the same.
TheuseMemo
hook will return the already computed memoized value without recomputation. - When the dependency values, a and b change.
The hook will recompute the value. - 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:
- Mengakses atau memanipulasi DOM, dan
- 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.
- 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.
- 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