bpdg
bpdg

Reputation: 181

ReactJS: TypeError: render is not a function updateContextConsumer

I'm trying to learn the Context API, and what I want to achieve, is showing the button Login and Sign up on the navigation but I had another error that had from another post I read the docs but me reading and not doing visually that how I learned by doing it making mistaking.

The buttons should open up two modal windows one login and sign up form.

Modal.js

import React from 'react';
import ReactDOM from "react-dom";
import Modal from "react-modal";
import ModalContext from '../Forms/ModalContext';


class ModalProvider extends React.Component {
  state = {
    loginOpened: false,
    signupOpened: false
  };

  openModal = modalType => () => {
    if (modalType === "login") {
      this.setState({
        loginOpened: true,
        signupOpened: false
      });
    } else if (modalType === "signup") {
      this.setState({
        loginOpened: false,
        signupOpened: true
      });
    }
  };

  closeModal = modalType => () => {
    if (modalType === "login") {
      this.setState({
        loginOpened: false
      });
    } else if (modalType === "signup") {
      this.setState({
        signupOpened: false
      });
    }
  };

  render(props) {

    return (
      <ModalContext.Provider value={{openModal: this.openModal, closeModal: this.closeModal}}>
        <Modal isOpen={loginOpened} onRequestClose={this.closeModal("login")}>
          <h1>Login</h1>
          <button onClick={this.openModal("signup")}>Open Signup</button>
          <button onClick={this.closeModal("login")}>Close this modal</button>
        </Modal>
        <Modal isOpen={signupOpened} onRequestClose={this.closeModal("signup")}>
           <h1>Sign Up</h1>
           <button onClick={this.openModal("login")}>Open Login</button>
           <button onClick={this.closeModal("signup")}>Close this modal</button>
        </Modal>
        {props.children}
      </ModalContext.Provider>
    )
  }
}

export default ModalProvider

ModalContext.js

I don't know why the person that helped me and explain did a very great job explain to but just want to know why it is just this line of code.

import {createContext} from 'react'

export default createContext()

Navigation.js

import React from 'react';
import { BrowserRouter as Router, Link } from 'react-router-dom';
import Dropdown from "../dropdowns/dropdowns"; 
import hamburger from "../images/menu.svg"
// This will display the login and sign up buttons
import ModalContext from '../Forms/ModalContext';

class Navigation extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            isExpanded: false
        };
    }

    handleToggle(e) {
        e.preventDefault();
        this.setState(prevState => ({
          isExpanded: !prevState.isExpanded, // negate the previous expanded state
        }));
      }


    render(props) {

        const { isExpanded } = this.state;

      return (
          <Router>
              <div className="NavbarContainer main">
                  <div className="mobilecontainer LeftNav">
                      <h2 className="BrandName LeftNav mobileboxmenu inline FarRight">Kommonplaces</h2>
                      <div className="hamburger inlinev" >
                          <img 
                            onClick={e => this.handleToggle(e)}
                            alt="menubtn" 
                            src={hamburger}
                          />
                      </div>
                  </div>

                  <div className={`NavBar collapsed ${isExpanded ? "is-expanded" : ""}`}>
                      <div className="col-a">
                        <Dropdown/>    
                        <li className="RightNav"><Link to="/">Host Your Space</Link></li>
                        <li className="RightNav"><Link to="/">About Us</Link></li>
                        <li className="RightNav"><Link to="/">Contact Us</Link></li>
                      </div>

                      <div className="col-c">
                        { /* 4. call the prop functions in `Navigation` component */ }

                        <ModalContext.Consumer>
                          {({openModal, closeModal}) => <button onClick={openModal("login")}>Login</button>}
                          {({openModal, closeModal}) => <button onClick={openModal('signup')}>Sign Up</button>}
                        </ModalContext.Consumer>
                      </div>
                  </div>
               </div>
         </Router>
      );
    }
}

  export default Navigation;

Upvotes: 1

Views: 1825

Answers (2)

HMR
HMR

Reputation: 39270

I was writing an example app as well so will post it as an answer:

function ToggleModal({ checked, onChange, modalId }) {
  console.log('rendering:', modalId);
  return (
    <label>
      {modalId}
      <input
        type="checkbox"
        checked={checked}
        onChange={onChange}
      />
    </label>
  );
}
const ToggleModalContainer = ({ modalId }) => {
  const { modals, changeModal } = React.useContext(State);
  const checked = modals[modalId];
  return React.useMemo(
    () =>
      ToggleModal({
        checked,
        modalId,
        onChange: () => changeModal(modalId, !checked),
      }),
    [changeModal, checked, modalId]
  );
};
function Modals() {
  const state = React.useContext(State);
  return Object.entries(state.modals).map(
    ([key, value]) =>
      value && <div key={key}>this is modal {key}</div>
  );
}

const State = React.createContext();
const App = () => {
  const [modals, setModals] = React.useState({
    a: false,
    b: false,
    c: false,
  });
  const changeModal = React.useCallback(
    (modalId, open) =>
      setModals(modals => ({ ...modals, [modalId]: open })),
    []
  );
  const state = React.useMemo(
    () => ({
      modals,
      changeModal,
    }),
    [changeModal, modals]
  );

  return (
    <State.Provider value={state}>
      <React.Fragment>
        {Object.keys(modals).map(modalId => (
          <ToggleModalContainer
            modalId={modalId}
            key={modalId}
          />
        ))}
        <Modals />
      </React.Fragment>
    </State.Provider>
  );
};

//render app
ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 0

Sushanth --
Sushanth --

Reputation: 55740

So you first created the ModalContext and the context gives you a Provider and a Consumer.

If you want to use the context for a Consumer, there should be a Provider providing it. For that to happen, the Consumer should be a child of the Provider so that it has access to it.

<Provider>
    ....
    ...
    <Consumer />
    ...
</Provider>

But in your case, Provider is not a ancestor of the Consumer.

Typically this is how this gets played out.

Navigation.js

import React from "react";
import { BrowserRouter as Router, Link } from "react-router-dom";
import Dropdown from "../dropdowns/dropdowns";
import hamburger from "../images/menu.svg";
// This will display the login and sign up buttons
import ModalContext from "../Forms/ModalContext";
import ModalProvider from "../Forms/ModalProvider";

class Navigation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isExpanded: false
    };
  }

  handleToggle(e) {
    e.preventDefault();
    this.setState(prevState => ({
      isExpanded: !prevState.isExpanded // negate the previous expanded state
    }));
  }

  render(props) {
    const { isExpanded } = this.state;

    return (
      <Router>
        {/* Modal provider provides the context to all the children */}
        <ModalProvider>
          <div className="NavbarContainer main">
            <div className="mobilecontainer LeftNav">
              <h2 className="BrandName LeftNav mobileboxmenu inline FarRight">
                Kommonplaces
              </h2>
              <div className="hamburger inlinev">
                <img
                  onClick={e => this.handleToggle(e)}
                  alt="menubtn"
                  src={hamburger}
                />
              </div>
            </div>

            <div
              className={`NavBar collapsed ${isExpanded ? "is-expanded" : ""}`}
            >
              <div className="col-a">
                <Dropdown />
                <li className="RightNav">
                  <Link to="/">Host Your Space</Link>
                </li>
                <li className="RightNav">
                  <Link to="/">About Us</Link>
                </li>
                <li className="RightNav">
                  <Link to="/">Contact Us</Link>
                </li>
              </div>

              <div className="col-c">
                {/* 4. call the prop functions in `Navigation` component */}

                {/* Consumer has access to context as children as function*/}
                <ModalContext.Consumer>
                  {({openModal, closeModal, loginOpened, signupOpened}) => {
                    return (
                      <React.Fragment>
                        <button onClick={openModal("login")}> Login</button>
                        <button onClick={openModal("signup")}>Sign Up</button>
                        <Modal
                          isOpen={loginOpened}
                          onRequestClose={closeModal("login")}
                        >
                          <h1>Login</h1>
                          <button onClick={openModal("signup")}>
                            Open Signup
                          </button>
                          <button onClick={closeModal("login")}>
                            Close this modal
                          </button>
                        </Modal>
                        <Modal
                          isOpen={signupOpened}
                          onRequestClose={closeModal("signup")}
                        >
                          <h1>Sign Up</h1>
                          <button onClick={openModal("login")}>
                            Open Login
                          </button>
                          <button onClick={closeModal("signup")}>
                            Close this modal
                          </button>
                        </Modal>
                      </React.Fragment>
                    );
                  }}
                </ModalContext.Consumer>
              </div>
            </div>
          </div>
        </ModalProvider>
      </Router>
    );
  }
}

export default Navigation;

ModalProvider.js

import React from "react";
import ReactDOM from "react-dom";
import Modal from "react-modal";
import ModalContext from "../Forms/ModalContext";

class ModalProvider extends React.Component {
  state = {
    loginOpened: false,
    signupOpened: false
  };

  openModal = modalType => () => {
    if (modalType === "login") {
      this.setState({
        loginOpened: true,
        signupOpened: false
      });
    } else if (modalType === "signup") {
      this.setState({
        loginOpened: false,
        signupOpened: true
      });
    }
  };

  closeModal = modalType => () => {
    if (modalType === "login") {
      this.setState({
        loginOpened: false
      });
    } else if (modalType === "signup") {
      this.setState({
        signupOpened: false
      });
    }
  };

  render(props) {
    return (
      <ModalContext.Provider
        value={{
          openModal: this.openModal,
          closeModal: this.closeModal,
          signupOpened: this.state.signupOpened,
          loginOpened: this.state.loginOpened,
        }}
      >
        {props.children}
      </ModalContext.Provider>
    );
  }
}

export default ModalProvider;

Upvotes: 1

Related Questions