I am working on my first react project and I got a bit stuck.
I am trying to create a navigation menu where when you click, for example, "men", a dropdown appears.
The issue I am having is that there shall only be one of these dropdowns showing at the same time, I have a total of 6 dropdowns.
I have been trying to remove the class from the elements and then re-adding it, but the issue is that it toggles the class back.
Here is the code:
const BottomNavigation = (props) => {
const [menShowing, setMenShowing] = useState(false);
const [womenShowing, setWomenShowing] = useState(false);
const [accessoriesShowing, setAccessoriesShowing] = useState(false);
const [shoesShowing, setShoesShowing] = useState(false);
const [faceBodyShowing, setFaceBodyShowing] = useState(false);
const [newInShowing, setNewInShowing] = useState(false);
const menHandler = () => setMenShowing((prevCheck) => !prevCheck);
const womenHandler = () => setWomenShowing((prevCheck) => !prevCheck);
const accessoriesHandler = () =>
setAccessoriesShowing((prevCheck) => !prevCheck);
const shoesHandler = () => setShoesShowing((prevCheck) => !prevCheck);
const faceBodyHandler = () => setFaceBodyShowing((prevCheck) => !prevCheck);
const newInHandler = () => setNewInShowing((prevCheck) => !prevCheck);
return (
<nav>
<div className={classes.bottom_navigation}>
<div className={classes.nav_items_container}>
<ul className={classes.nav_link}>
<li className={`${classes.nav_item}`}>
<p onClick={menHandler}>men</p>
<div
key="men"
className={`${classes.nav_dropdown} ${classes.dropdown__men} ${
menShowing ? classes.dropdown_show : null
}`}
>
<Men />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={womenHandler}>women</p>
<div
key="women"
className={`${classes.nav_dropdown} ${
classes.dropdown__women
} ${womenShowing ? classes.dropdown_show : null} `}
>
<Women />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={accessoriesHandler}>accessories</p>
<div
key="accessories"
className={`${classes.nav_dropdown} ${
classes.dropdown__accessories
} ${accessoriesShowing ? classes.dropdown_show : null} `}
>
<Accessories />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={shoesHandler}>shoes</p>
<div
key="shoes"
className={`${classes.nav_dropdown} ${
classes.dropdown__shoes
} ${shoesShowing ? classes.dropdown_show : null} `}
>
<Shoes />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={faceBodyHandler}>face + body</p>
<div
key="facebody"
className={`${classes.nav_dropdown} ${
classes.dropdown__facebody
} ${faceBodyShowing ? classes.dropdown_show : null} `}
>
<FaceBody />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={newInHandler}>new in</p>
<div
key="newin"
className={`${classes.nav_dropdown} ${
classes.dropdown__newin
} ${newInShowing ? classes.dropdown_show : null} `}
>
<NewIn />
</div>
</li>
</ul>
</div>
<div className={classes.options_container}>
<div className={classes.icon_container}>
<span className={classes.cart_sum}>$0.00</span>
<Cart className={classes.icon} />
</div>
<div className={classes.icon_container}>
<Heart className={classes.icon} />
</div>
<div className={classes.icon_container}>
<Visibility className={classes.icon} />
</div>
<div className={classes.icon_container}>
<User className={classes.icon} />
</div>
</div>
</div>
</nav>
);
};
If any other component is needed to solve this issue, let me know!
Instead of keeping every item's showing state in different useState's, you can use a single useState and a single menu click handler like:
const [showItem, setShowItem] = useState(null);
const menuClickHandler = (param) => {
if (showItem === param) {
setShowItem(null);
} else {
setShowItem(param);
}
};
and in your nav items you can call menu click handler and you can check if the current state is the name of current item like:
<li className={`${classes.nav_item}`}>
<p onClick={() => menuClickHandler("men")}>men</p>
<div
key="men"
className={`${classes.nav_dropdown} ${classes.dropdown__men} ${
showItem === "men" ? classes.dropdown_show : null
}`}
>
<Men />
</div>
</li>
You can take a look at this sandbox for this usage.
I highly recommend solving this by making a new component that you can reuse. I'll call mine Dropdown. This takes advantage of React's use of state. We want each dropdown to control it's own state of being open or not. Changing classes works, but I don't think you're properly leveraging what React can do.
Here's my quick version of a Dropdown, which you can test out here. The tech I use here is mostly React Hooks.
import "./dropdown.css";
import React, { useCallback, useEffect, useRef, useState } from "react";
const Dropdown = ({ onOpen, label, children }) => {
const [isOpen, setIsOpen] = useState(false);
const containerRef = useRef();
const onMouseDown = useCallback(
(e) => {
if (containerRef.current && !containerRef.current.contains(e.target)) {
setIsOpen(false);
}
},
[containerRef, setIsOpen]
);
useEffect(() => {
if (isOpen) {
onOpen();
}
}, [isOpen, onOpen]);
useEffect(() => {
window.addEventListener("mousedown", onMouseDown);
return () => window.removeEventListener("mousedown", onMouseDown);
}, [onMouseDown]);
const toggleIsOpen = () => {
setIsOpen(!isOpen);
}
return (
<div ref={containerRef} className="dropdown-container">
<div className="dropdown-label" onClick={toggleIsOpen}>{label}</div>
{isOpen && <div className="dropdown-content">{children}</div>}
</div>
);
};
export default Dropdown;
Here it is in action inside of a simple App.js
import React from "react";
import Dropdown from "./Dropdown.js";
function App() {
return (
<div className="App">
<div className="menu">
<Dropdown
label={<div>Men</div>}
onOpen={() => console.log("Opening men")}
>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
</Dropdown>
<Dropdown
label={<div>Women</div>}
onOpen={() => console.log("Opening women")}
>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
</Dropdown>
<Dropdown
label={<div>Shoes</div>}
onOpen={() => console.log("Opening shoes")}
>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
</Dropdown>
<Dropdown
label={<div>Accessories</div>}
onOpen={() => console.log("Opening accessories")}
>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
</Dropdown>
</div>
</div>
);
}
export default App;
Let me know if you have questions!
Related
I am trying to create selected tab in Next.Js.
The User will have the option to search for data it can be Users or Posts, what the user will search for will be selected by clicking on one of the buttons.
Once the user clicks on the button the button will change background to blue.
However I can't make it to work properly, when the User clicks on the button the .Selected class gets added to the button but the button doesn't render the CSS.
import React, { MouseEventHandler, ReactElement, useState } from 'react'
import { PageWithLayout } from '../components/Layouts/LayoutConfig'
import MainLayout from '../components/Layouts/MainLayout'
import style from '../styles/Search.module.css'
const Search: PageWithLayout = () => {
const [searchPosts, setPostsSearch] = useState < String > ();
const setSearchOption = (searchFor: String) => {
let searchOption = '';
if (searchFor == 'POSTS') {
searchOption = 'POSTS';
} else {
searchOption = 'USERS';
let button = document.getElementById('usersOption') as HTMLElement;
button.className += style.Selected;
}
console.log(searchOption);
setPostsSearch(searchOption);
}
return (
<>
<div className='pageContent'>
<div className={style.SearchBarContainer}>
<div className={style.SearchContainer}>
<i className="fa-solid fa-magnifying-glass"></i>
<input className={style.SearchBar} type={'text'} placeholder='Search...' />
</div>
<div className={style.SearchOptions}>
<button id='usersOption' onClick={() => setSearchOption('USERS')}>Users</button>
<button id='postsOption' onClick={() => setSearchOption('POSTS')}>Posts</button>
</div>
</div>
<div className='SearchedContent'>
</div>
</div>
</>
)
}
Search.getLayout = function getLayout(page: ReactElement) {
return (
<MainLayout>
{page}
</MainLayout>
)
}
export default Search
you can use searchOption data for className style
import React, { MouseEventHandler, ReactElement, useState } from 'react'
import { PageWithLayout } from '../components/Layouts/LayoutConfig'
import MainLayout from '../components/Layouts/MainLayout'
import style from '../styles/Search.module.css'
const Search: PageWithLayout = () => {
const [searchPosts, setPostsSearch] = useState<String>();
return (
<>
<div className='pageContent'>
<div className={style.SearchBarContainer}>
<div className={style.SearchContainer}>
<i className="fa-solid fa-magnifying-glass"></i>
<input className={style.SearchBar} type={'text'} placeholder='Search...'/>
</div>
<div className={style.SearchOptions}>
<button id='usersOption' className={searchPosts === 'USERS' ? style.Selected : undefined } onClick={() => setPostsSearch('USERS')}>Users</button>
<button id='postsOption' className={searchPosts === 'POSTS' ? style.Selected : undefined } onClick={() => setPostsSearch('POSTS')}>Posts</button>
</div>
</div>
<div className='SearchedContent'>
</div>
</div>
</>
)
}
Search.getLayout = function getLayout(page: ReactElement){
return(
<MainLayout>
{page}
</MainLayout>
)
}
export default Search
Just have a state for active searchOption and apply the class conditionally directly into the JSX.
const [activeSearchOption, setActiveSearchOption] = useState('USERS')
return (
<>
<div className='pageContent'>
<div className={style.SearchBarContainer}>
<div className={style.SearchContainer}>
<i className="fa-solid fa-magnifying-glass"></i>
<input className={style.SearchBar} type={'text'} placeholder='Search...'/>
</div>
<div className={style.SearchOptions}>
<button id='usersOption' className={activeSearchOption === 'USERS' ? 'active' : ''} onClick={() => setSearchOption('USERS')}>Users</button>
<button id='postsOption' className={activeSearchOption === 'POSTS' ? 'active' : ''} onClick={() => setSearchOption('POSTS')}>Posts</button>
</div>
</div>
<div className='SearchedContent'>
</div>
</div>
</>
)
I am getting the error "window is not defined" in nextJS project. Here isMobile is storing the value that window size is less than 767.98 or not to execute the open/close hamburger menu functionality. This code was working fine in ReactJS but not working in NextJS. Please help me to figure out this issue.
import Link from 'next/link';
import React, { useState, useEffect, useRef } from "react";
const Navbar = () => {
const isMobile = window.innerWidth <= 767.98;
const [isMenuOpen, setIsMenuOpen] = useState(!isMobile);
const toggle = () => isMobile && setIsMenuOpen(!isMenuOpen);
const ref = useRef()
useEffect(() => {
if (isMobile) {
const checkIfClickedOutside = (e) => {
if (!ref.current?.contains(e.target)) {
setIsMenuOpen(false);
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}
}, []);
return (
<>
<header>
<nav>
<div className="nav">
<div className="nav-brand">
<Link href="/" className="text-black"><a>Website</a></Link>
</div>
<div ref={ref}>
<div className="toggle-icon" onClick={toggle}>
<i id="toggle-button" className={isMenuOpen ? 'fas fa-times' : 'fas fa-bars'} />
</div>
{isMenuOpen && (
<div className={isMenuOpen ? "nav-menu visible" : "nav-menu"}>
<ul className="main-menu">
<li><Link href="/" onClick={toggle}><a>Home</a></Link></li>
<li><Link href="/blog" onClick={toggle}><a>Blog</a></Link></li>
<li className="drp">
<p className="dropbtn">Find <i className="fa-solid fa-angle-down"></i></p>
<ul className="dropdown-content">
<li><Link href="/find/portable-keyboards" onClick={toggle}><a>Portable Keyboards</a></Link></li>
</ul>
</li>
</ul>
</div>
)}
</div>
</div>
</nav>
</header>
</>
)
}
export default Navbar;
Next.js is a server-side rendering framework which means the initial call to generate HTML from the server. At this point, window object, is only available on the client-side (not on the server-side).
To solve this problem, you need to check window object availability.
import Link from 'next/link';
import React, { useState, useEffect, useRef } from "react";
const Navbar = () => {
const isMobile = typeof window !== "undefined" && window.innerWidth <= 767.98
const [isMenuOpen, setIsMenuOpen] = useState(!isMobile);
const toggle = () => isMobile && setIsMenuOpen(!isMenuOpen);
const ref = useRef()
useEffect(() => {
if (isMobile) {
const checkIfClickedOutside = (e) => {
if (!ref.current?.contains(e.target)) {
setIsMenuOpen(false);
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}
}, []);
return (
<>
<header>
<nav>
<div className="nav">
<div className="nav-brand">
<Link href="/" className="text-black"><a>Website</a></Link>
</div>
<div ref={ref}>
<div className="toggle-icon" onClick={toggle}>
<i id="toggle-button" className={isMenuOpen ? 'fas fa-times' : 'fas fa-bars'} />
</div>
{isMenuOpen && (
<div className={isMenuOpen ? "nav-menu visible" : "nav-menu"}>
<ul className="main-menu">
<li><Link href="/" onClick={toggle}><a>Home</a></Link></li>
<li><Link href="/blog" onClick={toggle}><a>Blog</a></Link></li>
<li className="drp">
<p className="dropbtn">Find <i className="fa-solid fa-angle-down"></i></p>
<ul className="dropdown-content">
<li><Link href="/find/portable-keyboards" onClick={toggle}><a>Portable Keyboards</a></Link></li>
</ul>
</li>
</ul>
</div>
)}
</div>
</div>
</nav>
</header>
</>
)
}
export default Navbar;
Another way you can fix it is you can move that window logic into useEffect (or componentDidMount on a class-based component)
import Link from 'next/link';
import React, { useState, useEffect, useRef } from "react";
const Navbar = () => {
const [isMobile, setIsMobile] = useState(false); //the initial state depends on mobile-first or desktop-first strategy
const [isMenuOpen, setIsMenuOpen] = useState(true);
const toggle = () => isMobile && setIsMenuOpen(!isMenuOpen);
const ref = useRef()
useEffect(() => {
setIsMobile(window.innerWidth <= 767.98)
setIsMenuOpen(window.innerWidth > 767.98)
}, [])
useEffect(() => {
if (isMobile) {
const checkIfClickedOutside = (e) => {
if (!ref.current?.contains(e.target)) {
setIsMenuOpen(false);
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}
}, [isMobile]);
return (
<>
<header>
<nav>
<div className="nav">
<div className="nav-brand">
<Link href="/" className="text-black"><a>Website</a></Link>
</div>
<div ref={ref}>
<div className="toggle-icon" onClick={toggle}>
<i id="toggle-button" className={isMenuOpen ? 'fas fa-times' : 'fas fa-bars'} />
</div>
{isMenuOpen && (
<div className={isMenuOpen ? "nav-menu visible" : "nav-menu"}>
<ul className="main-menu">
<li><Link href="/" onClick={toggle}><a>Home</a></Link></li>
<li><Link href="/blog" onClick={toggle}><a>Blog</a></Link></li>
<li className="drp">
<p className="dropbtn">Find <i className="fa-solid fa-angle-down"></i></p>
<ul className="dropdown-content">
<li><Link href="/find/portable-keyboards" onClick={toggle}><a>Portable Keyboards</a></Link></li>
</ul>
</li>
</ul>
</div>
)}
</div>
</div>
</nav>
</header>
</>
)
}
export default Navbar;
Note that, with this solution, your UI may have some flickering due to isMobile state
You can try this on your parent component definition.
import dynamic from 'next/dynamic'
const Navbar = dynamic(() => import('./Navbar'), { ssr: false });
const Parent = () => {
...
return (
{(typeof window !== 'undefined') &&
<Navbar/>
}
...
<Footer/>
);
}
Currently I have a component that is loaded when I call my API. This content has a CitizenshipType field that separates the items from each other. I have 2 buttons on top which I want to use to filter my data. 1 button is called Europe which should bring out all the content where CitizenshipType=Europe, etc. Currently I have all my data showing without any filtering. Here is my code:
Citizenship Page:
export default function Citizenship({ items, citi }) {
return (
<>
<div>
<div onClick=//SomeFunction>
CARRIBEAN
</div>
<div onClick=//SomeFunction>
EUROPE
</div>
</div>
<div>
<div onClick=//SomeFunction>
OTHER PROGRAMS
</div>
</div>
<div>
{items &&
items.map((item) => (
<div style={{ padding: "40px" }}>
<div class="citizen-item" key={item.id}>
<div className="container6">
<img
src={`http://localhost:1337${item.Thumbnail.url}`}
/>
<div>
{item.Title}
</div>
<div>
Access to {item.CountriesAccessible} countries
</div>
<div>
<button class="findButton">FIND OUT MORE</button>
</div>
</div>
</div>
</div>
))}
{citi &&
citi.map((test) => (
<div style={{ padding: "40px" }}>
<div class="citizen-item" key={test.id}>
<div className="container6">
<img
src={`http://localhost:1337${test.Thumbnail.url}`}
/>
<div>
{test.Title}
</div>
<div>
Access to {test.CountriesAccessible} countries
</div>
<div>
<button class="findButton">FIND OUT MORE</button>
</div>
</div>
</div>
</div>
))}
</>
);
}
Home Page where I am calling the APIs:
export default function Home({ items, citi }) {
return (
<div>
<Benefits />
<Citizenship items={items} citi={citi} />
<Video />
</div>
);
}
export async function getStaticProps() {
const CitizenshipEUres = await fetch(
"http://localhost:1337/citizenships?_limit=5&CitizenshipType=Europe"
);
const CitizenshipCAres = await fetch(
"http://localhost:1337/citizenships?_limit=5&CitizenshipType=Caribbien"
);
const items = await CitizenshipEUres.json();
const citi = await CitizenshipCAres.json();
return {
props: { items, citi },
};
}
you toggle them with states:
import React, { useState } from 'react'
export const TestComponent = () => {
const [carribeanIsShowing, setShowCarribean] = useState(false)
const [europeIsShowing, setShowEurope] = useState(false)
const toggleCarribean = () => {
if (!carribeanIsShowing) {
if(europeIsShowing) {
setShowEurope(false)
}
setShowCarribean(!carribeanIsShowing)
} else {
return
}
}
const toggleEurope = () => {
if (!europeIsShowing) {
if(carribeanIsShowing) {
setShowCarribean(false)
}
setShowEurope(!europeIsShowing)
} else {
return
}
}
return (
<div>
<button onClick={() => toggleCarribean()}>
CARRIBEAN
</button>
<button onClick={() => toggleEurope()}>
EUROPE
</button>
{europeIsShowing && <div>Europe</div>}
{carribeanIsShowing && <div>carribean</div>}
</div>
)
}
Create a new variable where you store the current CitizenshipType, with a default value of 'Europe'.
const [currentCitizenshipType, setCurrentCitizenshipType] = useState(
"Europe"
);
You change your onClick event
<div onClick={() => setCurrentCitizenshipType('Europe')}>
EUROPE
</div>
And finally add a filter statment to your items.map call:
{
items
.filter((item) => item.citizenshipType === currentCitizenshipType)
.map((item)
...}
This code:
How to display a dialog when a button is clicked using react and typescript?
I wanna open dialog from each todos, how to make it ? I used react js and typescript. Help me to resolve this problem.
interface ListProps {
todos: INote[];
onDelete: (title: string) => void;
}
const TodoList: React.FunctionComponent<ListProps> = ({ todos, onDelete }) => {
const [showAlert, setShowAlert] = useState(false);
const [todo, setTodos] = useState(null);
How to select each item by ts?It doesn't work. What is reason? Thanks!
const handleOpenDialog = (todos: any) => {
setTodos(todos);
setShowAlert(true);
};
const handleCloseDialog = () => {
setShowAlert(false);
};
return (
<>
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
{showAlert && todo && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</div>
))}
</section>
</>
);
};
export default TodoList;
just add a condition to only show the AlertDialog on selected todos
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
{showAlert && todos.title===todo?.title && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</div>
))}
</section>
or just move the AlertDialog outside the map
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
</div>
))}
{showAlert && todo && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</section>
How to make sure that only the secret text is displayed on click, in the panel of which the "show" button was clicked. Now two panels are revealed at once ...
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [visible, setVisible] = useState(true);
const onHandleVisible = () => {
setVisible(!visible);
};
return (
<div className="App">
<div className="panel">
<h3>Panel1</h3>
<button onClick={onHandleVisible}>Show</button>
{!visible ? (
<div>
<h1>I am secret text</h1>
</div>
) : null}
</div>
<div className="panel">
<h3>Panel2</h3>
<button onClick={onHandleVisible}>Show</button>
{!visible ? (
<div>
<h1>I am secret text 2</h1>
</div>
) : null}
</div>
</div>
);
}
https://codesandbox.io/s/frosty-nash-nx4v6
make 2 states
const [visible1, setVisible1] = useState(true);
const [visible2, setVisible2] = useState(true);
return (
<div className="App">
<div className="panel">
<h3>Panel1</h3>
<button onClick={() => setVisible1(!visible1)}>Show</button>
{!visible1 ? (
<div>
<h1>I am secret text</h1>
</div>
) : null}
</div>
<div className="panel">
<h3>Panel2</h3>
<button onClick={() => setVisible2(!visible2)}>Show</button>
{!visible2 ? (
<div>
<h1>I am secret text 2</h1>
</div>
) : null}
</div>
</div>
);
}
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [visible, setVisible] = useState(true);
const [ panel , setPanel ] = useState('Panel1');
const onHandleVisible = (value) => {
setPanel(value);
setVisible(!visible);
};
return (
<div className="App">
<div className="panel">
<h3>Panel1</h3>
<button onClick={() => onHandleVisible('Panel1')}>Show</button>
{!visible && panel === 'Panel1' ? (
<div>
<h1>I am secret text</h1>
</div>
) : null}
</div>
<div className="panel">
<h3>Panel2</h3>
<button onClick={() => onHandleVisible('Panel2')}>Show</button>
{!visible && panel === 'Panel2' ? (
<div>
<h1>I am secret text 2</h1>
</div>
) : null}
</div>
</div>
);
}
You can do with 1 state, just make that state as visiblePanel and update name of panel in that state to show any particular panel
https://codesandbox.io/s/ecstatic-dust-6vrv7
import "./styles.css";
export default function App() {
const [visiblePanel, setVisiblePanel] = useState(null);
const onHandleVisible = (panelName) => {
if(panelName === visiblePanel) {
setVisiblePanel(null);
} else {
setVisiblePanel(panelName);
}
};
return (
<div className="App">
<div className="panel">
<h3>Panel1</h3>
<button onClick={() => onHandleVisible('panel1')}>Show</button>
{visiblePanel === 'panel1' && (
<div>
<h1>I am secret text</h1>
</div>
)}
</div>
<div className="panel">
<h3>Panel2</h3>
<button onClick={() => onHandleVisible('panel2')}>Show</button>
{visiblePanel === 'panel2' && (
<div>
<h1>I am secret text 2</h1>
</div>
)}
</div>
</div>
);
}
You are using same variable for both use 2 variables to keep track which one is open.
https://codesandbox.io/s/blazing-fire-0dng2
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [visiblePanel1, setVisiblePanel1] = useState(true);
const [visiblePanel2, setVisiblePanel2] = useState(true);
const onHandleVisible = panel => {
if (panel === 1) {
setVisiblePanel1(!visiblePanel1);
} else if (panel === 2) {
setVisiblePanel2(!visiblePanel2);
}
};
return (
<div className="App">
<div className="panel">
<h3>Panel1</h3>
<button onClick={() => onHandleVisible(1)}>Show</button>
{!visiblePanel1 ? (
<div>
<h1>I am secret text</h1>
</div>
) : null}
</div>
<div className="panel">
<h3>Panel2</h3>
<button onClick={() => onHandleVisible(2)}>Show</button>
{!visiblePanel2 ? (
<div>
<h1>I am secret text 2</h1>
</div>
) : null}
</div>
</div>
);
}
Edit
If there are multiple panels
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [visible, setVisible] = useState(true);
const [panelName, setPanelName] = useState();
const onHandleVisible = panel => {
setVisible(!visible);
setPanelName(panel);
};
return (
<div className="App">
<div className="panel">
<h3>Panel1</h3>
<button onClick={() => onHandleVisible("panel1")}>Show</button>
{!visible && panelName === "panel1" ? (
<div>
<h1>I am secret text</h1>
</div>
) : null}
</div>
<div className="panel">
<h3>Panel2</h3>
<button onClick={() => onHandleVisible("panel2")}>Show</button>
{!visible && panelName === "panel2" ? (
<div>
<h1>I am secret text 2</h1>
</div>
) : null}
</div>
</div>
);
}
Also updated codesandbox.