React

React Logo

React Basics: useContext Hook

Use case: You have pieces of data that need to be shared globally amongst components (or, not even quite globally, just shared in different places in your application). Props drilling, for what it is... nah. Redux? Well, maybe but what an expensive solution if your application just doesn't need that kind of horsepower.... The useContext hook lets you share data among components as needed, it's easy to set up and from what I hear, it's rather performant. Word on the street is that Redux actually uses useContext hooks internally, but that's just a vicious rumor....

App.js

Simple, straightforward page layout that displays a header and a button. Nothing special, except they're different components.

//App.js
import React from 'react';
import TopNavbar from './components/Navbar';
import Footer from './components/Footer';
import ThemeContextProvider from './contexts/ThemeContext';
import ThemeToggle from './components/ThemeToggle';

function App() {
  
  return (
    <div className="home-page-anchor-div">
      <ThemeContextProvider>
        <Container fluid={true}>
          <TopNavbar />
          <Container className="home-page-toast p-6">
            <Row>
              some content here....
            </Row>
          </Container>
          <ThemeToggle />
        </Container >
        <Footer />
      </ThemeContextProvider>
    </div >
  );
}

export default App;

ThemeContext.js

While I won't yet refer to this file as "the store" this IS the file where the data lives that needs to be shared and/or displayed in multiple components. NOTE: The typical convention for this file is to name it with your subject matter in mind; this file decorates our theme hence "themeContext". If you need a bunch of data about pizza then you might use "pizzaContext" for the name.

//ThemeContext.js
import React, 
{ createContext, Component, useState } from 'react';

export const ThemeContext = createContext();

class ThemeContextProvider extends Component {
    state = {
        isLightTheme: true,
        light: 
        {syntax: 'black', ui: '#ddd', bg: '#61dafb'},
        dark: 
        {syntax: 'white', ui: '#333', bg: 'teal'}
    }
    toggleTheme = () => {
    this.setState({ isLightTheme: 
    !this.state.isLightTheme });
    }
    render() {
        return (
            <ThemeContext.Provider 
            value={{ ...this.state, 
            toggleTheme: this.toggleTheme }}>
                {this.props.children}
            </ThemeContext.Provider>
        );
    }
}

export default ThemeContextProvider;

ThemeToggle.js

I isolated the button (that changes the UI color theme) by itself. The actual toggleTheme method (called during onClick() lives in ThemeContext.js)

//ThemeToggle.js
import React, { useContext } from 'react';
import { ThemeContext } 
from '../contexts/ThemeContext';

function ThemeToggle() {
    const contextType = 
    useContext(ThemeContext);
    const { toggleTheme } = contextType;
    return (
        <button onClick={toggleTheme}>
            Toggle Theme
        </button>
    )
}

export default ThemeToggle

Navbar.js

...standard, regulation navbar kinda' stuff, but I'm using React's context to determine the theme to show. (Note the inline style calls....)

//Navbar.js
import React, { useContext } from 'react';
import { Container, Navbar, NavbarBrand, 
Nav, NavItem, NavLink } from 'reactstrap';
import { ThemeContext } from '../contexts/ThemeContext';

function TopNavbar() {
    //declare contextType from the imported ThemeContext 
    //file to make the values in state available via context
    const contextType = useContext(ThemeContext);
    //console.log(contextType);
    //destructure values from contextType
    const { isLightTheme, light, dark } = contextType;
    const theme = isLightTheme ? light : dark;
    return (
        <Navbar color="faded" light expand="md" 
        className="home-page-navbar" 
        style=
        {{ background: theme.bg, color: theme.syntax }}>
            <Container 
            className="home-page-navbar-container">
                <NavbarBrand 
                href="/" style=
                {{ color: theme.syntax }}>
                    richleach.com
                </NavbarBrand>
                <Nav 
                className="ml-auto" navbar>
                    <NavItem 
                    className="d-flex align-items-center">
                        <NavLink 
                        className="font-weight-bold" 
                        href="/" 
                        style=
                        {{ color: theme.syntax }}>
                        Nav stuff
                        </NavLink>
                    </NavItem>
                    <NavItem 
                    className="d-flex align-items-center">
                        <NavLink 
                        className="font-weight-bold" 
                        style={{ color: theme.syntax }}>
                            rich@cfsnap.com
                        </NavLink>
                    </NavItem>
                </Nav>
            </Container>
        </Navbar>
    )
}

export default TopNavbar;