Halo semuanya! Nama saya Arthur, saya bekerja di VKontakte sebagai tim web seluler, saya terlibat dalam proyek
VKUI - pustaka komponen React, dengan bantuan yang sebagian antarmuka kami dalam aplikasi seluler ditulis. Masalah bekerja dengan negara global masih terbuka untuk kita. Ada beberapa pendekatan terkenal: Redux, MobX, Context API. Saya baru-baru ini menemukan sebuah artikel oleh
Manajemen Negara AndrΓ© Gardi
dengan React Hooks - No Redux atau Context API , di mana penulis menyarankan untuk menggunakan React Hooks untuk mengontrol keadaan aplikasi.
Hook dengan cepat menembus kehidupan pengembang, menawarkan cara-cara baru untuk memecahkan atau memikirkan kembali tugas dan pendekatan yang berbeda. Mereka mengubah pemahaman kita tentang tidak hanya bagaimana mendeskripsikan komponen, tetapi juga bagaimana bekerja dengan data. Baca terjemahan artikel dan komentar penerjemah di bawah kucing.

React Hooks Lebih Kuat dari yang Anda kira
Hari ini kita akan mempelajari React Hooks dan mengembangkan hook kustom untuk mengelola status global aplikasi, yang akan lebih sederhana daripada implementasi Redux dan lebih produktif daripada Context API.
Bereaksi Dasar-dasar Hooks
Anda dapat melewati bagian ini jika Anda sudah terbiasa dengan kait.
useState ()
Sebelum munculnya kait, komponen fungsional tidak memiliki kemampuan untuk mengatur keadaan lokal. Situasi telah berubah dengan munculnya
useState()
.

Panggilan ini mengembalikan array. Elemen pertamanya adalah variabel yang menyediakan akses ke nilai status. Elemen kedua adalah fungsi yang memperbarui keadaan dan menggambar ulang komponen untuk mencerminkan perubahan.
import React, { useState } from 'react'; function Example() { const [state, setState] = useState({counter:0}); const add1ToCounter = () => { const newCounterValue = state.counter + 1; setState({ counter: newCounterValue}); } return ( <div> <p>You clicked {state.counter} times</p> <button onClick={add1ToCounter}> Click me </button> </div> ); }
useEffect ()
Komponen kelas merespons efek samping menggunakan metode siklus hidup seperti
componentDidMount()
.
useEffect()
memungkinkan Anda melakukan hal yang sama dalam komponen fungsional.
Secara default, efek dipicu setelah setiap redraw. Tetapi Anda dapat memastikan bahwa mereka dieksekusi hanya setelah mengubah nilai variabel tertentu, memberikan mereka parameter opsional kedua dalam bentuk array.
Untuk mencapai hasil yang mirip dengan
componentDidMount()
, kami akan meneruskan array kosong ke parameter kedua. Karena isi dari array kosong selalu tidak berubah, efeknya akan dieksekusi hanya sekali.
Berbagi negara
Kami melihat bahwa keadaan kait berfungsi seperti keadaan komponen kelas. Setiap instance komponen memiliki keadaan internalnya sendiri.
Untuk berbagi status antar komponen, kami akan membuat kait kami sendiri.

Idenya adalah untuk menciptakan berbagai pendengar dan hanya satu negara. Setiap kali komponen berubah status, semua komponen berlangganan memanggil
getState()
mereka
getState()
dan diperbarui karena ini.
Kita dapat mencapai ini dengan memanggil
useState()
di dalam kait kustom kami. Tetapi alih-alih mengembalikan fungsi
setState()
, kami menambahkannya ke array pendengar dan mengembalikan fungsi yang memperbarui objek keadaan secara internal dan memanggil semua pendengar.
Tunggu sebentar. Bagaimana itu membuat hidup saya lebih mudah?
Ya kamu benar Saya membuat
paket NPM yang merangkum semua logika yang dijelaskan.
Anda tidak harus mengimplementasikannya di setiap proyek. Jika Anda tidak lagi ingin menghabiskan waktu membaca dan ingin melihat hasil akhir, cukup tambahkan paket ini ke aplikasi Anda.
npm install -s use-global-hook
Untuk memahami cara bekerja dengan paket, pelajari contoh-contoh dalam dokumentasi. Dan sekarang saya mengusulkan untuk fokus pada bagaimana paket itu diatur di dalam.
Versi pertama
import { useState, useEffect } from 'react'; let listeners = []; let state = { counter: 0 }; const setState = (newState) => { state = { ...state, ...newState }; listeners.forEach((listener) => { listener(state); }); }; const useCustom = () => { const newListener = useState()[1]; useEffect(() => { listeners.push(newListener); }, []); return [state, setState]; }; export default useCustom;
Gunakan dalam komponen
import React from 'react'; import useCustom from './customHook'; const Counter = () => { const [globalState, setGlobalState] = useCustom(); const add1Global = () => { const newCounterValue = globalState.counter + 1; setGlobalState({ counter: newCounterValue }); }; return ( <div> <p> counter: {globalState.counter} </p> <button type="button" onClick={add1Global}> +1 to global </button> </div> ); }; export default Counter;
Versi ini sudah menyediakan status berbagi. Anda dapat menambahkan jumlah counter sewenang-wenang ke aplikasi Anda, dan mereka semua akan memiliki keadaan global yang sama.
Tapi kita bisa berbuat lebih baik
Apa yang kamu inginkan:
- menghapus pendengar dari array ketika melepas komponen;
- membuat kait lebih abstrak untuk digunakan dalam proyek lain;
- mengelola
initialState
menggunakan parameter; - tulis ulang pengait dengan gaya yang lebih fungsional.
Memanggil fungsi sesaat sebelum melepas komponen
Kami telah menemukan bahwa memanggil
useEffect(function, [])
dengan array kosong berfungsi dengan cara yang sama seperti
componentDidMount()
. Tetapi jika fungsi yang dilewatkan dalam parameter pertama mengembalikan fungsi lain, maka fungsi kedua akan dipanggil tepat sebelum melepas komponen. Persis seperti
componentWillUnmount()
.
Jadi, dalam kode fungsi kedua, Anda dapat menulis logika untuk menghapus komponen dari berbagai pendengar.
const useCustom = () => { const newListener = useState()[1]; useEffect(() => {
Versi kedua
Selain pembaruan ini, kami juga merencanakan:
- lulus Bereaksi parameter dan singkirkan impor;
- ekspor bukan customHook, tetapi fungsi yang mengembalikan customHook dengan initalState yang diberikan;
- membuat objek
store
yang akan berisi nilai state
dan fungsi setState()
; - ganti fungsi panah dengan yang biasa di
setState()
dan useCustom()
sehingga Anda dapat mengaitkan store
dengan this
.
function setState(newState) { this.state = { ...this.state, ...newState }; this.listeners.forEach((listener) => { listener(this.state); }); } function useCustom(React) { const newListener = React.useState()[1]; React.useEffect(() => {
Pisahkan tindakan dari komponen
Jika Anda pernah bekerja dengan perpustakaan manajemen negara yang kompleks, maka Anda tahu bahwa memanipulasi keadaan global dari komponen bukanlah ide yang baik.
Akan lebih benar untuk memisahkan logika bisnis dengan menciptakan tindakan untuk mengubah keadaan. Oleh karena itu, saya ingin versi terbaru dari paket menyediakan akses komponen bukan ke
setState()
, tetapi ke serangkaian tindakan.
Untuk melakukan ini, kami menyediakan
useGlobalHook(React, initialState, actions)
kami
useGlobalHook(React, initialState, actions)
argumen ketiga. Hanya ingin menambahkan beberapa komentar.
- Tindakan akan memiliki akses ke
store
. Dengan cara ini, tindakan dapat membaca konten store.state
, memperbarui store.setState()
memanggil store.setState()
dan bahkan memanggil store.actions
lain dengan store.actions
. - Untuk menghindari kekacauan, objek tindakan mungkin berisi sub-objek. Dengan demikian, Anda dapat mentransfer
actions.addToCounter(amount
) ke subobjek dengan semua tindakan balasan: actions.counter.add(amount)
Versi final
Cuplikan berikut adalah versi saat ini dari paket NPM
use-global-hook
.
function setState(newState) { this.state = { ...this.state, ...newState }; this.listeners.forEach((listener) => { listener(this.state); }); } function useCustom(React) { const newListener = React.useState()[1]; React.useEffect(() => { this.listeners.push(newListener); return () => { this.listeners = this.listeners.filter(listener => listener !== newListener); }; }, []); return [this.state, this.actions]; } function associateActions(store, actions) { const associatedActions = {}; Object.keys(actions).forEach((key) => { if (typeof actions[key] === 'function') { associatedActions[key] = actions[key].bind(null, store); } if (typeof actions[key] === 'object') { associatedActions[key] = associateActions(store, actions[key]); } }); return associatedActions; } const useGlobalHook = (React, initialState, actions) => { const store = { state: initialState, listeners: [] }; store.setState = setState.bind(store); store.actions = associateActions(store, actions); return useCustom.bind(store, React); }; export default useGlobalHook;
Contoh Penggunaan
Anda tidak lagi harus berurusan dengan
useGlobalHook.js
. Sekarang Anda dapat fokus pada aplikasi Anda. Berikut ini adalah dua contoh penggunaan paket.
Banyak Penghitung, Satu Nilai
Tambahkan penghitung sebanyak yang Anda inginkan: semuanya akan memiliki nilai global. Setiap kali salah satu penghitung akan meningkatkan negara global, yang lainnya akan digambar ulang. Dalam hal ini, komponen induk tidak perlu digambar ulang.
Contoh hidup .
Permintaan ajax asinkron
Cari repositori GitHub berdasarkan nama pengguna. Kami memproses permintaan ajax secara asinkron menggunakan async / menunggu. Kami memperbarui penghitung kueri dengan setiap pencarian baru.
Contoh hidup .
Yah, itu saja
Kami sekarang memiliki perpustakaan manajemen negara kami sendiri di React Hooks.
Komentar Penerjemah
Sebagian besar solusi yang ada pada dasarnya adalah perpustakaan yang terpisah. Dalam hal ini, pendekatan yang dijelaskan oleh penulis menarik karena hanya menggunakan fitur Bereaksi bawaan. Selain itu, dibandingkan dengan API Konteks yang sama, yang juga keluar dari kotak, pendekatan ini mengurangi jumlah redraw yang tidak perlu dan karenanya menang dalam kinerja.