navigation Navigation


Context Verwendung


Die Verwaltung von Zuständen in React-Anwendungen stellt Entwickler oft vor Herausforderungen, besonders wenn Daten über mehrere Komponenten hinweg geteilt werden müssen. Das klassische Prop-Drilling, bei dem Daten durch mehrere Komponenten-Ebenen hindurchgereicht werden, führt schnell zu unübersichtlichem und schwer wartbarem Code.

Die Context API von React bietet hier eine elegante Lösung. Mit ihr lassen sich Daten zentral verwalten und an beliebige Komponenten in der Anwendung weitergeben - ohne den Umweg über Props. Besonders bei Funktionalitäten wie Themes, Benutzereinstellungen oder Authentifizierungsdaten zeigt sich die Stärke dieser Methode.

Inhaltsverzeichnis

    Grundstruktur

    Zuerst erstellen wir die nötige Grundstruktur, damit es besser verständlich ist. Dieses Beispiel organisiere ich in einem Ordner.

    Folgende Struktur möchte ich erzeugen.

    • ThemeSample/: Ordner
      • ThemeSampleStyles.jsx: Hier definieren wir Stile
      • ThemeSampleContext.jsx: Hier wird der Context definiert
      • ThemeSample.jsx: Das Haupt-Component
      • SubOne.jsx: Component eins
      • SubTwo.jsx: Component zwei
      • SubThree.jsx: Component drei
      • SubFour.jsx: Component vier

    Context

    In diesem Beispiel machen wir es möglich, dass die einzelnen Components ein globales Theme umschalten (setzen) können. Für diesen Zweck verwenden wir createContext und useContext. Entsprechend müssen wir irgendwo das zugehörige Context-Objekt erzeugen. Das machen wir in der ThemeSampleContext.jsx.

    ThemeSampleContext.jsx
    import { createContext } from 'react';
    
    const ThemeSampleContext  = createContext({
        theme: 'light',
        updateTheme: () => {}
    });
    
    export default ThemeSampleContext;

    Wir geben gleich einen Startwert in Form eines Objektes mit, welches den aktuellen Wert (Namen) des Themes und eine Funktion, die wir später für die Aktualisierung des Themes verwenden werden.

    Das ist unser Start-Objekt (Start-Wert).

    createContext({
        theme: 'light',
        updateTheme: () => {}
    });

    Styles

    Ich halte in diesem Beispiel die Stile begrenzt. Sie spielen nicht die wichtigste Rolle. Aber ich zeige hier eine Möglichkeit von mehreren, wie man eigene Components gestalten kann.

    Für dieses Vorhaben verwende ich Styled Components Bibliothek, welche uns eine tolle Möglichkeit zum Gestalten unserer Components und der Elemente innerhalb von Components gibt.

    Hier nochmals eine kurze Referenz, wie man das Packet installiert.

    npm install --save styled-components

    Meine Styled Components speichere ich in einem Ordner und verwende sie an den entsprechenden Stellen. Dadurch halte ich meine Components etwas aufgeräumter.

    In diesem Beispiel möchte ich meinem Haupt-Component, dem Container, eine paar bestimmte Stile hinzufügen und den einzelnen Components auch, sodass sie identisch aussehen.

    In meinem Ordner erstelle ich die Datei ThemeSampleStyles.jsx. Dort werden die Styled Components erstellt.

    ThemeSampleStyles.jsx
    import styled from 'styled-components';
    
    const ThemeSampleDiv = styled.div`
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-gap: 10px;
    `;
    
    const SubComWrapper = styled.div`
        border: 2px solid;
        height: 100%;
        padding: 20px;
        box-sizing: border-box;
    
        .component_content {
            display: flex;
            justify-content: center;
            align-items: center;
        }
    
        p {
            display: block;
            width: 100%;
        }
    `;
    
    export {
        ThemeSampleDiv,
        SubComWrapper
    };

    Ich exportiere in meinen JavaScript (JSX) Dateien immer am Ende. So kann ich an einer Stelle was aus dieser Datei exportiert wurde und was nicht. Sind die Exporte an verschiedenen Stellen in der Datei verteilt und diese Datei ist etwas größer, muss ich ggf. genauer schauen und suchen, was exportiert ist.


    Components

    Ok, bis jetzt haben die beiden Dateien für Context ThemeSampleContext.jsx und für Stile ThemeSampleStyles.jsx. Sie beide sind voneinander unabhängig und machen tatsächlich auch nicht viel.

    An diesen Punkt möchte ich die einzelnen Components erstellen. Ohne größere Funktion, als Grundgerüst für unsere weitere Entwicklung. Das einzige, was ich gleich in diesem Schritt mache, ist die Verwendung von Styled Component für die Einzel-Components, als Wrapper.

    ThemeSample/SubOne.jsx
    // Styles
    import { SubComWrapper } from './ThemeSampleStyles';
    
    const SubOne = () => {
        return (
            <SubComWrapper className="sub_component">
                <h3>Component 1</h3>
                <div className="component_content">
                    <p>Current theme:</p>
                    <button>Set theme one</button>
                </div>
            </SubComWrapper>
        );
    };
    
    export default SubOne;
    ThemeSample/SubTwo.jsx
    // Styles
    import { SubComWrapper } from './ThemeSampleStyles';
    
    const SubTwo = () => {
        return (
            <SubComWrapper className="sub_component">
                <h3>Component 2</h3>
                <div className="component_content">
                    <p>Current theme:</p>
                    <button>Set theme two</button>
                </div>
            </SubComWrapper>
        );
    };
    
    export default SubTwo;
    ThemeSample/SubThree.jsx
    // Styles
    import { SubComWrapper } from './ThemeSampleStyles';
    
    const SubThree = () => {
        return (
            <SubComWrapper className="sub_component">
                <h3>Component 3</h3>
                <div className="component_content">
                    <p>Current theme:</p>
                    <button>Set theme three</button>
                </div>
            </SubComWrapper>
        );
    };
    
    export default SubThree;
    ThemeSample/SubFour.jsx
    // Styles
    import { SubComWrapper } from './ThemeSampleStyles';
    
    const SubFour = () => {
        return (
            <SubComWrapper className="sub_component">
                <h3>Component 4</h3>
                <div className="component_content">
                    <p>Current theme:</p>
                    <button>Set theme four</button>
                </div>
            </SubComWrapper>
        );
    };
    
    export default SubFour;

    Diese Components binde ich nun in der ThemeSample.jsx Datei ein. Ebenfalls verwende ich auch in dieser Haupt-Datei mein Styled Component, um meine Stile anzuwenden.

    ThemeSample/ThemeSample.jsx
    // Styles
    import { ThemeSampleDiv } from './ThemeSampleStyles';
    
    // Components
    import SubOne from './SubOne';
    import SubTwo from './SubTwo';
    import SubThree from './SubThree';
    import SubFour from './SubFour';
    
    const ThemeSample = () => {
        return (
            <>
                <div style={{
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'space-between'
                }}>
                    <p style={{ backgroundColor: '#ddd', padding: '10px' }}>
                        Current theme: 
                    </p>
                    <button>Reset theme</button>
                </div>
                <ThemeSampleDiv className="theme_sample">
                    <SubOne />
                    <SubTwo />
                    <SubThree />
                    <SubFour />
                </ThemeSampleDiv>
            </>
        );
    };
    
    export default ThemeSample;

    Damit steht unsere Grundstruktur und wir können mit dem Einbau des Context und des States starten.

    React Context Beispiel - Grundstruktur ohne Context und State


    Context Provider und State

    Wie wir wissen, müssen wir an einer bestimmten Stelle (möglichst weit oben im Komponentenbaum) unseren Context-Provider definieren, welcher alle Components einschließt, die Zugriff auf die Daten im Context-Objekt benötigen. In diesem Beispiel setzen wir alle einzelnen Components hinein.

    Außerdem definieren wir eine einfache Zustandsverwaltung, welche uns Hilft das aktuell aktive Theme zu überwachen und zu aktualisieren.

    ThemeSample/ThemeSample.jsx
    // React
    import { useState, useContext } from 'react';
    
    // Styles
    import { ThemeSampleDiv } from './ThemeSampleStyles';
    
    // Components
    import SubOne from './SubOne';
    import SubTwo from './SubTwo';
    import SubThree from './SubThree';
    import SubFour from './SubFour';
    
    const ThemeSample = () => {
        const { currentTheme } = useContext(ThemeSampleContext);
        const [theme, setTheme] = useState(currentTheme || 'light');
    
        const handleThemeUpdate = newTheme => {
            setTheme(current => newTheme);
        };
    
        return (
            <ThemeSampleContext.Provider value={{
                theme: theme,
                updateTheme: handleThemeUpdate
            }}>
                <div style={{
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'space-between'
                }}>
                    <p style={{ backgroundColor: '#ddd', padding: '10px' }}>
                        Current theme: {currentTheme}
                    </p>
                    <button>Reset theme</button>
                </div>
                <ThemeSampleDiv className="theme_sample">
                    <SubOne />
                    <SubTwo />
                    <SubThree />
                    <SubFour />
                </ThemeSampleDiv>
            </ThemeSampleContext.Provider>
        );
    };
    
    export default ThemeSample;

    Wir haben also nun alle unsere Components innerhalb folgender Struktur angesiedelt.

    <ThemeSampleContext.Provider value={{
        theme: theme,
        updateTheme: handleThemeUpdate
    }}>
        ...
    </ThemeSampleContext.Provider>

    Die Eigenschaft updateTheme in unserem Context-Objekt erhält eine Referenz auf die handleThemeUpdate Funktion. Das bedeutet, dass bei jedem Aufruf von updateTheme eigentlich die handleThemeUpdate Funktion aufgerufen wird. Diese Funktion ruft wiederrum die Funktion unserer Zustandsverwaltung setTheme auf und aktualisiert den Wert.


    Context in Components

    Im letzten Schritt müssen wir nur noch die Verwendung von Context-Objekt und die ein Button-Klick-Event in den einzelnen Components implementieren.

    Ich mache es anhand dem Beispiel für ThemeSample/SubOne.jsx. Analog sollten alle anderen Components ebenfalls erweitert werden.

    Wir benötigen die useContext Funktion, um an den Wert des Context-Objektes heranzukommen. Außerdem müssen das Context-Objekt an sich importieren, da wir der useContext Funktion mitteilen müssen, aus welchem Objekt gelesen werden soll.

    ThemeSample/SubOne.jsx
    // React
    import { useContext } from 'react';
    
    // Context
    import ThemeSampleContext from '/ThemeSampleContext';
    
    // Styles
    import { SubComWrapper } from './ThemeSampleStyles';
    
    const SubOne = () => {
        const { theme, updateTheme } = useContext(ThemeSampleContext);
    
        const onThemeSwitch = () => {
            updateTheme('theme-one');
        };
    
        return (
            <SubComWrapper className="sub_component">
                <h3>Component 1</h3>
                <div className="component_content">
                    <p>Current theme: <strong>{theme}</strong></p>
                    <button onClick={onThemeSwitch}>Set theme one</button>
                </div>
            </SubComWrapper>
        );
    };
    
    export default SubOne;

    Wenn man alle übrigen Components nach dem gleichen Schema erweitert, erhält man ein Ergebnis, bei dem jedes Component das globale Theme auslesen und aktualisieren kann.

    React Context Beispiel - Funktion mit State und Context