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.

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.

TypeScript 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.

TypeScript 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.

TypeScript 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

/ Weiter

Zurück zu Context

Zur Übersicht