navigation Navigation


Provider


Der React Context Provider ist ein zentraler Bestandteil des Context-API-Systems und dient dazu, Daten und Zustände im Komponentenbaum für alle darunterliegenden Komponenten verfügbar zu machen. Er umschließt die Komponenten, die auf den geteilten Kontext zugreifen sollen, und stellt sicher, dass Änderungen am Zustand automatisch und effizient an alle Konsumenten weitergegeben werden. Dadurch lässt sich die Weitergabe von Props durch viele Komponentenebenen hinweg vermeiden, was den Code übersichtlicher und wartbarer macht. React Context Provider ist besonders nützlich für globale Einstellungen, Authentifizierungszustände oder Theme-Informationen, die an verschiedenen Stellen der Anwendung benötigt werden.

Inhaltsverzeichnis

    Aufbau

    Der Provider ist ein Component, das aus dem Context-Objekt stammt und die Daten für alle untergeordneten Components bereitstellt. Er definiert, welcher Wert im Context verfügbar sein soll.

    Syntax

    Die Verwendung eines Context-Providers sieht folgendermaßen aus.

    <MyContext.Provider value={/* Irgendein Wert */}>
        ...
    </MyContext.Provider>

    Das value Prop ist entscheidend. Es besimmt, welcher Wert an die Komponenten weitergegeben wird, die den Context konsumieren, also Daten aus dem Context auslesen.

    Beispiel - einfache Werte

    Damit das Thema etwas greifbarer wird, bauen wir ein Beispiel auf, in welchem wir zwei Button-Components definieren. Ein Button-Component wird unseren Context-Provider verwenden und das andere Button-Component wird ohne Context-Provider eingesetzt.

    Wir nennen unser Beispiel ThemedButtonExample.jsx.

    ThemedButtonExample.jsx
    // React
    import { createContext, useContext, useState } from 'react';
    
    // Context mit einem Startwert
    const ThemeContext = createContext('light');
    
    const ThemedButton = () => {
    
        // Verwendung des Context-Objekts
        const theme = useContext(ThemeContext);
    
        const buttonStyles = {
            color: theme === 'dark' ? '#fffff' : '#00000',
            backgroundColor: theme === 'dark' ? '#450373' : 'lightblue'
        };
    
        return (
            <button className={theme} style={buttonStyles}>
                Ein {theme} button
            </button>
        );
    
    };
    
    const ThemedButtonExample = () => {
        // State Definition
        const [theme, setTheme] = useState('dark');
    
        return (
            <>
                <ThemeContext.Provider value={theme}>
                    <p><ThemedButton /></p>
                </ThemeContext.Provider>
    
                <p><ThemedButton /></p>
    
                <button onClick={() => setTheme(current => theme === 'dark' ? 'light' : 'dark')}>
                    Theme wechseln
                </button>
            </>
        );
    };
    
    export default ThemedButtonExample;

    Als Ergebnis erhalten wir zwei Buttons. Ein Button kann auf die Werte/Daten des Context-Objektes zugreifen, weil dieser innerhalb des ThemeContext.Provider platziert ist und der andere Button vom gleichen Component-Typ nicht.

    Beim Klick auf den Button “Theme wechseln” sehen wir, dass nur ein Button auf die Aktualisierung des Themes reagiert.

    React Context - Themed Button Beispiel 1
    Initialer Zustand
    React Context Objekt - Themed Button Beispiel 2
    Aktualisierter Zustand

    Provider Wrapper

    Wenn man komplexere Werte hat und ggf. auch eine Zustandsverwaltung im gleichen Zusammenhang verwenden möchte, besteht die Möglichkeit einen eigenen Provider-Wrapper zu erstellen, welcher dann als tatsächlicher Wrapper um die Components eingesetzt wird.

    Dieser eigener Provider-Wrapper gibt aber den tatsächlichen Context-Provider zurück. Im inneren die children Prop verwendet, um einfach alles zu umgeben, was man an Elementen im CustomProviderWrapper platziert.

    Hier ein schematischer Aufbau.

    Schematischer Aufbau
    function CustomProviderWrapper({ children }) {
        ...
    
        return (
            <MyContext.Provider value={value}>
                {children}
            </MyContext.Provider>
        );
    }
    
    function App() {
        return (
            <CustomProviderWrapper>
                <Header />
                <MainContent />
            </CustomProviderWrapper>
        );
    }

    Ein großer Vorteil von diesem Ansatz ist, dass man einige Funktionen und die Zustandsverwaltung innerhalb von diesem eigenen Context-Provider-Wrapper definieren kann. Das hält unsere Components sauber.

    Provider Wrapper - Beispiel

    Erstellen wir ein Beispiel, indem wir diesen Ansatz verwenden und erproben. Wir nennen unsere Datei CustomProviderWrapper.jsx.

    Wichtiger Part von dieser Datei wird UserProvider sein. Das ist unser Provider-Wrapper, welcher den Zustand und ein paar Funktionen, welche wir als Wert an das tatsächliche Context-Objetk binden.

    CustomProviderWrapper.jsx
    // React
    import {
        createContext,
        useContext,
        useState
    } from 'react';
    
    // Context-Object definieren
    const UserContext = createContext();
    
    // Eigener Provider Wrapper
    function UserProvider({ children }) {
        
        // State definieren
        const [user, setUser] = useState(null);
    
        // Login Funktion
        const login = (username) => {
            setUser({ name: username, isLoggedIn: false });
        };
    
        // Logout Funktion
        const logout = () => {
            setUser(null);
        }
    
        // Inhalt des Context-Objekts
        const value = {
            user,
            login,
            logout
        };
    
        // Rückgabe des tatsächlichen Providers
        return (
            <UserContext.Provider value={value}>
                {children}
            </UserContext>
        );
    
    }
    
    // Hilfs-Component (Header)
    function Header() {
        
        // Context auslesen/verwenden
        const { user, login, logout } = useContext(UserContext);
    
        // Login Button Klick-Event definieren
        const onLoginAction = () => {
            login('John');
        };
    
        // Logout Button Klick-Event definieren
        const onLogoutAction = () => {
            logout();
        };
    
        return (
            <header>
                <p>
                    <button onClick={onLoginAction}>
                        Login
                    </button>
                </p>
                <p>
                    <button onClick={onLogoutAction}>
                        Logout
                    </button>
                </p>
            </header>
        );
    
    }
    
    // Hilfs-Component (MainContent)
    function MainContent() {
    
        // Context-Objekt auslesen - nur Benutzer
        const { user } = useContext(UserContext);
    
        return (
            <main>
                {user ? (
                    <>
                        <p>User: {user.name}</p>
                        <p>Is logged in: {user.isLoggedIn ? 'Yes' : 'No'}</p>
                    </>
                ) : (
                    <p>Bitte anmelden</p>
                )}
            </main>
        );
    
    }
    
    // Haupt-Component - Verwendung des Provider-Wrappers
    function CustomProviderWrapper() {
        return (
            <UserProvider>
                <Header />
                <MainContent />
            </UserProvider>
        );
    }
    
    export default CustomProviderWrapper;

    Als Ergebnis haben wir zwei Buttons und eine optionale Ausgabe von Benutzerdaten, wenn wir die Aktion “Login” ausführen. Beim “Abmelden” setzt sich der Wert zurück.

    React Context - Custom Provider Wrapper - Beispiel Ergebnis