Membuat Toko Global seperti Redux Menggunakan React Hooks

Halo, Habr! Saya mempersembahkan kepada Anda terjemahan artikel "Bangun Toko Global Redux-like Menggunakan React Hooks" oleh Ramsay.


Mari kita bayangkan bahwa saya menulis pengantar yang menarik untuk artikel ini, dan sekarang kita bisa langsung ke hal-hal yang sangat menarik. Singkatnya, kita akan melakukannya
gunakan useReducer dan useContext untuk membuat hook React kustom yang menyediakan akses ke repositori global yang mirip dengan Redux.


Saya sama sekali tidak menganggap bahwa solusi ini setara dengan Redux, karena saya yakin tidak. Dengan mengatakan "Redux-like," Maksudku,
bahwa Anda akan memperbarui repositori menggunakan pengiriman dan tindakan , yang akan mengubah status repositori dan mengembalikan salinan baru dari keadaan termutasi.
Jika Anda belum pernah menggunakan Redux, pura-pura tidak membaca paragraf ini.


Kait


Mari kita mulai dengan membuat konteks ( selanjutnya Konteks ) yang akan berisi negara kita ( selanjutnya menyatakan ) dan fungsi pengiriman ( selanjutnya dikirim ). Kami juga akan membuat fungsi useStore , yang akan berperilaku seperti hook kami.


// store/useStore.js import React, { createContext, useReducer, useContext } from "react"; //     const initialState = {} const StoreContext = createContext(initialState); // useStore    React       export const useStore = store => { const { state, dispatch } = useContext(StoreContext); return { state, dispatch }; }; 

Karena semuanya disimpan di dalam Konteks Bereaksi , Anda perlu membuat Penyedia yang akan memberi
kita objek negara dan fungsi pengiriman . Penyedia adalah tempat kami menggunakan useReducer .


 // store/useStore.js ... const StoreContext = createContext(initialState); export const StoreProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); return ( <StoreContext.Provider value={{ state, dispatch }}> {children} </StoreContext.Provider> ); }; ... 

Kami menggunakan useReducer untuk mendapatkan status dan pengiriman . Sebenarnya, ini persis apa yang useReducer lakukan. Selanjutnya, kami menyampaikan status dan mengirimkannya ke Penyedia .
Sekarang kita dapat membungkus komponen Bereaksi menggunakan <Penyedia /> dan komponen ini dapat menggunakan useStore untuk berinteraksi dengan repositori.


Kami belum membuat peredam . Ini akan menjadi langkah kita selanjutnya.


 // store/useStore.js ... const StoreContext = createContext(initialState); //    actions,     state const Actions = {}; // reducer   ,  action    dispatch // action.type -  ,     Actions //   update   state      const reducer = (state, action) => { const act = Actions[action.type]; const update = act(state); return { ...state, ...update }; }; ... 

Saya penggemar berat tindakan memisahkan dan menyatakan ke dalam kelompok logis, misalnya: Anda mungkin perlu memantau keadaan penghitung (contoh klasik dari penerapan penghitung) atau keadaan pengguna (apakah pengguna telah masuk ke sistem atau preferensi pribadinya).
Dalam beberapa komponen, Anda mungkin memerlukan akses ke kedua status ini, sehingga gagasan untuk menyimpannya dalam satu penyimpanan global masuk akal. Kita dapat membagi tindakan kita menjadi grup logis seperti userActions dan countActions , yang membuat pengelolaannya jauh lebih mudah.


Mari kita buat file countActions.js dan userActions.js di folder store.


 // store/countActions.js export const countInitialState = { count: 0 }; export const countActions = { increment: state => ({ count: state.count + 1 }), decrement: state => ({ count: state.count - 1 }) }; 

 // store/userActions.js export const userInitialState = { user: { loggedIn: false } }; export const userActions = { login: state => { return { user: { loggedIn: true } }; }, logout: state => { return { user: { loggedIn: false } }; } }; 

Di kedua file ini, kami mengekspor initialState , karena kami ingin menggabungkannya nanti di file useStore.js menjadi objek initialState tunggal.
Kami juga mengekspor objek Tindakan yang menyediakan fungsi untuk mutasi negara. Perhatikan bahwa kami tidak mengembalikan objek keadaan baru, karena kami ingin ini terjadi dalam peredam , dalam file useStore.js .


Sekarang kita mengimpor semuanya ke useStore.js untuk mendapatkan gambaran lengkap.


 // store/useStore.js import React, { createContext, useReducer, useContext } from "react"; import { countInitialState, countActions } from "./countActions"; import { userInitialState, userActions } from "./userActions"; //    (initial states) const initialState = { ...countInitialState, ...userInitialState }; const StoreContext = createContext(initialState); //  actions const Actions = { ...userActions, ...countActions }; const reducer = (state, action) => { const act = Actions[action.type]; const update = act(state); return { ...state, ...update }; }; export const StoreProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); return ( <StoreContext.Provider value={{ state, dispatch }}> {children} </StoreContext.Provider> ); }; export const useStore = store => { const { state, dispatch } = useContext(StoreContext); return { state, dispatch }; }; 

Kami berhasil! Buat lingkaran kehormatan, dan ketika Anda kembali, kita akan melihat bagaimana menggunakannya semua dalam komponen.


Selamat datang kembali! Saya harap lingkaran Anda benar-benar terhormat. Mari kita lihat useStore beraksi.


Pertama kita dapat membungkus komponen Aplikasi kita di <StoreProvider /> .


 // App.js import React from "react"; import ReactDOM from "react-dom"; import { StoreProvider } from "./store/useStore"; import App from "./App"; function Main() { return ( <StoreProvider> <App /> </StoreProvider> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<Main />, rootElement); 

Kami membungkus Aplikasi di StoreProvider sehingga komponen anak memiliki akses ke nilai dari penyedia. Nilai ini adalah status dan pengiriman .


Sekarang, mari kita asumsikan bahwa kita memiliki komponen AppHeader yang memiliki tombol masuk / keluar.


 // AppHeader.jsx import React, {useCallback} from "react"; import { useStore } from "./store/useStore"; const AppHeader = props => { const { state, dispatch } = useStore(); const login = useCallback(() => dispatch({ type: "login" }), [dispatch]); const logout = useCallback(() => dispatch({ type: "logout" }), [dispatch]); const handleClick = () => { loggedIn ? logout() : login(); } return ( <div> <button onClick={handleClick}> {loggedIn ? "Logout" : "Login"}</button> <span>{state.user.loggedIn ? "logged in" : "logged out"}</span> <span>Counter: {state.count}</span> </div> ); }; export default AppHeader; 

Tautan ke Kode Sandbox dengan implementasi penuh


Penulis asli: Ramsay
Tautan ke aslinya

Source: https://habr.com/ru/post/id460083/


All Articles