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 initialStatemenggunakan 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 storeyang akan berisi nilaistatedan fungsisetState();
- ganti fungsi panah dengan yang biasa di setState()danuseCustom()sehingga Anda dapat mengaitkanstoredenganthis.
 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 kontenstore.state, memperbaruistore.setState()memanggilstore.setState()dan bahkan memanggilstore.actionslain denganstore.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.