Reputation: 719
I'm coming from a Vue background and I'm having a really hard time understanding how to show something conditionally when the HTML is split into multiple parts.
Suppose I got the following structure:
import React, { useState } from "react";
const [mobileNavOpen, setMobileNavOpen] = useState(false);
function Home() {
return (
<div>
<button
onClick={(): void => setMobileNavOpen(true)}
type="button"
className="btn"
>
X
</button>
{mobileNavOpen && <MobileNav />}
</div>
);
}
function MobileNav() {
return (
<div>
<button
onClick={(): void => setMobileNavOpen(false)}
type="button"
className="btn"
>
X
</button>
</div>
);
}
export default Home;
How would I be able to access [mobileNavOpen, setMobileNavOpen] in both Home() and MobileNav(). Basically what I want to achieve is a Home Component where users can press a button upon which a MobileMenu Component opens with yet another button they can use to close the menu again.
Right now, the 2nd line from the top const [mobileNavOpen, setMobileNavOpen] = useState(false);
results in Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
but where would I put it?
Should this line be in the Home() Component and all child components emit an event to show or close the Menu? Or do I need a state management library for something as simple as this? What is the "React" way to do this?
Upvotes: 1
Views: 6843
Reputation: 66
if your Home() Component
renders MobileNav() Component
you should put const [mobileNavOpen, setMobileNavOpen] = useState(false)
in Home()
like:
const Home = () => {
const [mobileNavOpen, setMobileNavOpen] = useState(false)
return (
<>
...
<MobileNav
handleMobileNav={mobileNavOpen}
/>
...
</>)
}
const MobileNav = ({ handleMobileNav }) => {
return <></>
}
Upvotes: 2
Reputation: 9002
A simple way would be to pass the state modifier down to the child component. For this the hook needs to be moved inside the parent component.
import React, { useState } from "react";
function Home() {
const [mobileNavOpen, setMobileNavOpen] = useState(false);
return (
<div>
<button
onClick={(): void => setMobileNavOpen(true)}
type="button"
className="btn"
>
X
</button>
{mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen} />}
</div>
);
}
function MobileNav(setMobileNavOpen) {
return (
<div>
<button
onClick={(): void => setMobileNavOpen(false)}
type="button"
className="btn"
>
X
</button>
</div>
);
}
export default Home;
Alternatively, you could abstract this a little so that your child component defines only what's required:
import React, { useState } from "react";
function Home() {
const [mobileNavOpen, setMobileNavOpen] = useState(false);
return (
<div>
<button
onClick={(): void => setMobileNavOpen(true)}
type="button"
className="btn"
>
X
</button>
{mobileNavOpen && <MobileNav onClose={() => setMobileNavOpen(false)} />}
</div>
);
}
function MobileNav(onClose) {
return (
<div>
<button
onClick={(): void => onClose()}
type="button"
className="btn"
>
X
</button>
</div>
);
}
export default Home;
The docs provide some good best practices for managing shared state.
Upvotes: 1
Reputation: 333
Should this line be in the Home() Component and all child components emit an event to show or close the Menu?
Yes. Then pass state to the MobileNavComponent
Upvotes: 0
Reputation: 2328
import React, { useState } from "react";
function Home() {
const [mobileNavOpen, setMobileNavOpen] = useState(false);
return (
<div>
<button
onClick={() => setMobileNavOpen(true)}
type="button"
className="btn"
>
X
</button>
{mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen} />}
</div>
);
}
function MobileNav({setMobileNavOpen}) {
return (
<div>
<button
onClick={() => setMobileNavOpen(false)}
type="button"
className="btn"
>
X
</button>
</div>
);
}
export default Home;
Upvotes: 4