4

I've got a few React functional Components that I would like to share a state. In this example two toggle buttons that would conditionally show/hide a searchbar and a navbar.

--Solution, based on the accepted answer, on the bottom--

I'm completely new to useContext() and I keep running into the following error in the console: Uncaught TypeError: setSearchbarToggle is not a function This goes for both buttons.

Bellow I have a filtered example code. It is just for the example I use the states in one file. In real life I would re-use the states in multiple functional components.

This is my header.js

import React, { useState, useContext } from "react"
import "./header.sass"
import { Context } from "./HeaderContext"

export const Header = () => {

  const headerContext = useContext(Context)
  const { navbarToggle, setNavbarToggle, searchbarToggle, setSearchbarToggle } = headerContext

  return (
    <React.Fragment>
      <div className={"sticky-top"}>

        <button onClick={ () => setNavbarToggle( !navbarToggle )}> Toggle Menu </button>
        <button onClick={ () => setSearchbarToggle( !searchbarToggle )}> Toggle Search </button>

        {navbarToggle && <h3>Menu is showing</h3>}
        {searchbarToggle && <h3>Searchbar is showing</h3>}

      </div>
    </React.Fragment>
  )
}
export default Header

And this is my HeaderContext.jsx

import React, { createContext, useState } from "react";
import PropTypes from "prop-types";

export const Context = createContext({});

export const Provider = props => {
  const {
    navbarToggle: initialNavBarToggle,
    searchbarToggle: initialSarchbarToggle,
    children
  } = props;
  
  const [navbarToggle, setNavbarToggle] = useState(initialNavBarToggle);
  const [searchbarToggle, setSearchbarToggle] = useState(initialSarchbarToggle);

  const headerContext = {
    navbarToggle, setNavbarToggle,
    searchbarToggle, setSearchbarToggle
  };

  return <Context.Provider value={headerContext}>{children}</Context.Provider>;
};

export const { Consumer } = Context;

Provider.propTypes = {
  navbarToggle: PropTypes.bool,
  searchbarToggle: PropTypes.bool
};

Provider.defaultProps = {
  navbarToggle: false,
  searchbarToggle: false
};

I hope you can shed some light on this for me

--edit--

This is my code based on the accepted answer.

import React, { useContext } from "react"
import { Provider,Context } from "./HeaderContext"

export const HeaderWithContext= () => {

  const headerContext = useContext(Context)
  const { navbarToggle, setNavbarToggle, searchbarToggle, setSearchbarToggle } = headerContext

  return (
    <React.Fragment>
      <div className={"sticky-top"}>

        <button onClick={ () => setNavbarToggle( !navbarToggle )}> Toggle Menu </button>
        <button onClick={ () => setSearchbarToggle( !searchbarToggle )}> Toggle Search </button>

        {navbarToggle && <h3>Menu is showing</h3>}
        {searchbarToggle && <h3>Searchbar is showing</h3>}

      </div>
    </React.Fragment>
  )
}

export const Header = () => {
  return (
    <Provider>
      <HeaderWithContext/>
    </Provider>
  )
};
Community
  • 1
  • 1
Tim
  • 861
  • 1
  • 12
  • 27
  • one of the parent components, e.g. App, must `import { Provider } from "./HeaderContext"` and wrap the header (or one of its ancestor components) with `
    `... have you done that?
    – Aprillion May 01 '20 at 09:39
  • @Aprillion , No I have not. Let me check that. I will be right back – Tim May 01 '20 at 09:42
  • Oh, that was quicker than expected. It works just the way you explained. Could you place this as an answer so I can accept it? :) Also, do you happen to know where I can find a better read on this? – Tim May 01 '20 at 09:44
  • 1
    I added a link to documentation in my answer - there are examples on the same page ;) – Aprillion May 01 '20 at 09:47

1 Answers1

5

One of the parent components, e.g. App, must wrap the header (or one of its ancestor components) with Context.Provider:

import { Provider } from "./HeaderContext"
...
<Provider>
  <Header />
</Provider>  
Aprillion
  • 21,510
  • 5
  • 55
  • 89
  • Thanks, as mentioned before. It worked like a charm. Man, was I going around in circles – Tim May 01 '20 at 09:53