Erstellen eines Redux-ähnlichen globalen Speichers mithilfe von React Hooks

Hallo Habr! Ich präsentiere Ihnen die Übersetzung des Artikels "Erstellen eines Redux-ähnlichen globalen Geschäfts mit React Hooks" von Ramsay.


Stellen wir uns vor, ich hätte eine interessante Einführung zu diesem Artikel geschrieben, und jetzt können wir direkt zu den wirklich interessanten Dingen gehen. Kurz gesagt, wir werden
Verwenden Sie useReducer und useContext , um einen benutzerdefinierten React-Hook zu erstellen, der den Zugriff auf ein globales Repository ähnlich wie Redux ermöglicht.


Ich gehe in keiner Weise davon aus, dass diese Lösung das volle Äquivalent von Redux ist, da ich sicher bin, dass dies nicht der Fall ist. Mit "Redux-like" meine ich:
dass Sie das Repository mithilfe von Versand und Aktionen aktualisieren, wodurch der Status des Repositorys mutiert und eine neue Kopie des mutierten Status zurückgegeben wird.
Wenn Sie Redux noch nie verwendet haben, tun Sie einfach so, als würden Sie diesen Absatz nicht lesen.


Haken


Beginnen wir mit der Erstellung eines Kontexts ( im Folgenden: Kontext ), der unseren Status ( im Folgenden: Status ) und eine Versandfunktion ( im Folgenden: Versand ) enthält. Wir werden auch die useStore- Funktion erstellen, die sich wie unser Hook verhält.


// 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 }; }; 

Da alles im Reaktionskontext gespeichert ist, müssen Sie einen Provider erstellen, der gibt
uns ein Zustandsobjekt und eine Versandfunktion . Bei Provider verwenden wir 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> ); }; ... 

Wir verwenden useReducer , um den Status und den Versand abzurufen . Genau das macht useReducer . Als nächstes übergeben wir den Status und den Versand an den Anbieter .
Jetzt können wir jede React-Komponente mit <Provider /> umbrechen und diese Komponente kann useStore verwenden, um mit dem Repository zu interagieren.


Wir haben noch keinen Reduzierer erstellt. Dies wird unser nächster Schritt sein.


 // 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 }; }; ... 

Ich bin ein großer Fan davon, Aktionen und Status in logische Gruppen zu unterteilen, zum Beispiel: Möglicherweise müssen Sie den Status des Zählers (ein klassisches Beispiel für die Implementierung des Zählers) oder den Status des Benutzers (unabhängig davon, ob sich der Benutzer beim System angemeldet hat oder seine persönlichen Einstellungen) überwachen.
In einigen Komponenten benötigen Sie möglicherweise Zugriff auf beide Status, sodass die Idee, sie in einem einzigen globalen Repository zu speichern, durchaus sinnvoll ist. Wir können unsere Aktionen in logische Gruppen wie userActions und countActions aufteilen , was die Verwaltung erheblich vereinfacht.


Erstellen wir die Dateien countActions.js und userActions.js im Speicherordner.


 // 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 } }; } }; 

In diesen beiden Dateien exportieren wir initialState , da wir sie später in der Datei useStore.js zu einem einzigen initialState- Objekt kombinieren möchten .
Wir exportieren auch ein Actions-Objekt, das Funktionen für Zustandsmutationen bereitstellt. Beachten Sie, dass wir kein neues Statusobjekt zurückgeben , da dies im Reduzierer in der Datei useStore.js geschehen soll .


Jetzt importieren wir alles in useStore.js , um das vollständige Bild zu erhalten.


 // 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 }; }; 

Wir haben es geschafft! Bilden Sie einen Ehrenkreis, und wenn Sie zurückkehren, werden wir sehen, wie alles in der Komponente verwendet wird.


Willkommen zurück! Ich hoffe, Ihr Kreis war wirklich ehrenwert. Werfen wir einen Blick auf useStore in Aktion.


Zuerst können wir unsere App- Komponente in <StoreProvider /> einbinden .


 // 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); 

Wir verpacken die App in den StoreProvider, sodass die untergeordnete Komponente Zugriff auf den Wert des Anbieters hat. Dieser Wert ist sowohl Status als auch Versand .


Nehmen wir nun an, wir haben eine AppHeader- Komponente mit einer Anmelde- / Abmeldeschaltfläche.


 // 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; 

Link zur Code Sandbox mit vollständiger Implementierung


Ursprünglicher Autor: Ramsay
Link zum Original

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


All Articles