Reputation: 181
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
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
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