Vertical menu in react - reactjs

I need to have a vertical menu like below picture, in react
here is my code:
function DropDownMenu({className}) {
const [isOpen, setIsopen] = useState(false);
const toggleDropDown = () => {
setIsopen(isOpen => !isOpen)
}
return (
<nav className={styles.nav}>
<ul>
<li onClick={toggleDropDown} ><a href='#settings'>item1</a>
{isOpen && <ul>
<li><a href='#settings'>sub-item1</a></li>
<li><a href='#settings'>sub-item2</a></li>
<li><a href='#settings'>sub-item-3</a></li>
</ul>
}
</li>
<li onClick={toggleDropDown} ><a href='#message'>item3</a>
{isOpen &&
<ul>
<li><a href='#settings'>sub-item1</a></li>
<li><a href='#settings'>sub-item2</a></li>
</ul>
}
</li>
</ul>
</nav>
);
}
It lacks hide/show each item separately, and an arrow with some animation when it opens/closes

You should create a separate component called MenuItem and isolate the toggle logic.
const MenuItem = ({ menuTitle, subMenus = [] }) => {
const [isOpen, setIsopen] = useState(false);
const toggleDropDown = () => {
setIsopen((isOpen) => !isOpen);
};
return (
<li onClick={toggleDropDown}>
{menuTitle}
{isOpen && (
<ul>
{subMenus.map((item) => (
<li>
{item}
</li>
))}
</ul>
)}
</li>
);
};
Then use MenuItem in DropDownMenu
function DropDownMenu({ className }) {
return (
<nav>
<ul>
<MenuItem
menuTitle="item1"
subMenus={["sub-item1", "sub-item2", "sub-item-3"]}
/>
<MenuItem menuTitle="item3" subMenus={["sub-item1", "sub-item2"]} />
</ul>
</nav>
);
}
Code sandbox

Related

React toggle body class when state is changed

I have a menu button on my site that when I click it I am toggling the visibility of the site menu. How can I also toggle a class on the site Body element when the menu button is visible, so that I can prevent vertical scrolling?
// App.js
function App() {
return (
<div className="App">
<Header />
</div>
);
}
--
// Header.js
function Header() {
// Set menu button default state
const [active, setActive] = useState(false);
return (
<>
// Menu Button
<button
onClick={() => setActive(!active)}
type="button">
<span className="is-block">{active ? 'Close' : 'Menu'}</span>
</button>
// Menu Links
<div className={active ? 'c-navigation is-active' : 'c-navigation'}>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
<li>Link 4</li>
</div>
</>
)
}
doing something like this:
document.body.style.overflow = "hidden";
and don't worry you're not manipulating the DOM that React is rendering, you're manipulating its parent.
function Header() {
// Set menu button default state
const [active, setActive] = useState(false);
useEffect(() => {
if (active) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "unset";
}
}, [active]);
return (
<>
// Menu Button
<button onClick={() => setActive(!active)} type="button">
<span className="is-block">{active ? "Close" : "Menu"}</span>
</button>
// Menu Links
<div className={active ? "c-navigation is-active" : "c-navigation"}>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
<li>Link 4</li>
</div>
</>
);
}

How can I add an active class when a button is clicked in ReactJS

What i want to acheive is to have an active class when a link is clicked. I'm using react
This is the pagination.js file where I want to have the active class on each number clicked
const Pagination = ({ recordsPerPage, totalRecords, paginate }) => {
const pageNumbers = []
for(let i = 1; i <= Math.ceil(totalRecords / recordsPerPage); i++) {
pageNumbers.push(i)
}
return (
<ul className='pagination center'>
<li class="disabled"><i class="material-icons">chevron_left</i></li>
{pageNumbers.map(number => (
<li key={number} style={{ marginLeft: '5px'}}>
<a href='#!' className='hoverable' onClick={() => paginate(number)}>
{number}
</a>
</li>
))}
<li class="waves-effect"><i class="material-icons">chevron_right</i></li>
</ul>
)
}
You can just add a state here, when your onclick fire, set the state of current index
const Pagination = ({ recordsPerPage, totalRecords, paginate }) => {
const [activePage, setActivePage] = useState(0);
const pageNumbers = []
for(let i = 1; i <= Math.ceil(totalRecords / recordsPerPage); i++) {
pageNumbers.push(i)
}
const handlePaginate = (index) => {
setActivePage(index);
paginate(index)
}
return (
<ul className='pagination center'>
<li class="disabled"><i class="material-icons">chevron_left</i></li>
{pageNumbers.map(number => (
<li key={number} style={{ marginLeft: '5px'}} className={activePage === number && 'active'}>
<a href='#!' className='hoverable' onClick={() => handlePaginate(number)}>
{number}
</a>
</li>
))}
<li class="waves-effect"><i class="material-icons">chevron_right</i></li>
</ul>
)
}
As for initial state, right now just default to 0, but it should be whatever current page you are on.

How Can I set State in stateless Function, in a nested Function?

I know that I can't change the state in a stateless function when the function is nested, though I am only this far away to set the state in order for the page work as I would want. I want to keep this as a functional component. The issue is below I want to set the language under this function, or any other way works I know how to do this with Class component, though unless there will be 10 votes to do that would prefer to stay with functional.
function changeTounge(e) {
console.log("Write something!!");
console.log(e.currentTarget.dataset.id);
console.log(e.currentTarget.dataset.value);
//setLanguage({ iso2: "lv", speak: "Latviešu" });
}
the full code is:
import React, { useState } from "react";
import { Link } from "react-router-dom";
export function PrimaryNav() {
const [language, setLanguage] = useState({ iso2: "us", speak: "English" });
return (
<div className="primary-nav">
<ul className="navigation">
<li className="active">
<Link to="/">Home</Link>
</li>
<li className="has-child">
Opportunities
<div className="wrapper">
<div id="nav-homepages" className="nav-wrapper">
<ul>
{OpsOption("RealEstate", "Real Estate")}
{OpsOption("Vehicles", "Vehicles")}
{OpsOption("JobSearch", "Job Search")}
{OpsOption("PersonalCompany", "Personal Company")}
{OpsOption("Inves`enter code here`tInIdea", "Invest In Idea")}
</ul>
</div>
</div>
</li>
<li>
<Link to="/contact">Help & Support</Link>
</li>
<li className="has-child language">
{ActiveLanguage(language.iso2, language.speak)}
<div className="wrapper">
<div id="nav-languages" className="nav-wrapper">
<ul>
{LanguageOption("English", "us")}
{LanguageOption("British", "gb")}
{LanguageOption("Latviešu", "lv")}
{LanguageOption("Estonia", "ee")}
{LanguageOption("Lithuania", "lt")}
{LanguageOption("Russian", "ru")}
{LanguageOption("Deutch", "de")}
{LanguageOption("French", "fr")}
</ul>
</div>
</div>
</li>
</ul>
</div>
);
}
function LanguageOption(label, classLabel) {
return (
<li
onClick={changeTounge.bind(this)}
data-id={label}
data-value={classLabel}
>
<Link to="#nav-locations">
<span className={"flag-icon flag-icon-" + classLabel}></span>
{" "}
{label}
</Link>
</li>
);
}
function changeTounge(e) {
console.log("Write something!!");
console.log(e.currentTarget.dataset.id);
console.log(e.currentTarget.dataset.value);
//setLanguage({ iso2: "lv", speak: "Latviešu" });
}
function OpsOption(pageLink, label) {
return (
<li>
<Link to={"/" + pageLink}>{label}</Link>
</li>
);
}
function ActiveLanguage(iso2, activeLanguage) {
return (
<a href="#nav-languages">
<span className={"flag-icon flag-icon-" + iso2}></span> {activeLanguage}
</a>
);
}
export default PrimaryNav;
Try nesting the components inside of the main component, they'll be able to access and set its state. This is still a functional pattern here.
The other option would be to add a prop to your children components, and pass in the setLanguage function through that prop.
Also, you don't need the changeTounge.bind.this in functional react :D. In general, if you're using this at all in functional react... that's a red flag.
import React, { useState } from "react";
import { Link } from "react-router-dom";
const PrimaryNav = () => {
const [language, setLanguage] = useState({ iso2: "us", speak: "English" });
const changeTounge = (e) => {
console.log("Write something!!");
console.log(e.currentTarget.dataset.id);
console.log(e.currentTarget.dataset.value);
setLanguage({ iso2: "lv", speak: "Latviešu" });
}
const OpsOption = (pageLink, label) => {
return (
<li>
<Link to={"/" + pageLink}>{label}</Link>
</li>
);
}
const ActiveLanguage = (iso2, activeLanguage) => {
return (
<a href="#nav-languages">
<span className={"flag-icon flag-icon-" + iso2}></span> {activeLanguage}
</a>
);
}
const LanguageOption = (label, classLabel) => {
return (
<li
onClick={changeTounge}
data-id={label}
data-value={classLabel}
>
<Link to="#nav-locations">
<span className={"flag-icon flag-icon-" + classLabel}></span>
{" "}
{label}
</Link>
</li>
);
}
return (
<div className="primary-nav">
<ul className="navigation">
<li className="active">
<Link to="/">Home</Link>
</li>
<li className="has-child">
Opportunities
<div className="wrapper">
<div id="nav-homepages" className="nav-wrapper">
<ul>
{OpsOption("RealEstate", "Real Estate")}
{OpsOption("Vehicles", "Vehicles")}
{OpsOption("JobSearch", "Job Search")}
{OpsOption("PersonalCompany", "Personal Company")}
{OpsOption("Inves`enter code here`tInIdea", "Invest In Idea")}
</ul>
</div>
</div>
</li>
<li>
<Link to="/contact">Help & Support</Link>
</li>
<li className="has-child language">
{ActiveLanguage(language.iso2, language.speak)}
<div className="wrapper">
<div id="nav-languages" className="nav-wrapper">
<ul>
{LanguageOption("English", "us")}
{LanguageOption("British", "gb")}
{LanguageOption("Latviešu", "lv")}
{LanguageOption("Estonia", "ee")}
{LanguageOption("Lithuania", "lt")}
{LanguageOption("Russian", "ru")}
{LanguageOption("Deutch", "de")}
{LanguageOption("French", "fr")}
</ul>
</div>
</div>
</li>
</ul>
</div>
);
}
export default PrimaryNav;
There are probably tidier ways to do some of this, but this seems to work:
import React, { useState } from "react";
import { Link } from "react-router-dom";
export function PrimaryNav() {
const [language, setLanguage] = useState({ iso2: "us", speak: "English" });
return (
<div className="primary-nav">
<ul className="navigation">
<li className="active">
<Link to="/">Home</Link>
</li>
<li className="has-child">
Opportunities
<div className="wrapper">
<div id="nav-homepages" className="nav-wrapper">
<ul>
{OpsOption("RealEstate", "Real Estate")}
{OpsOption("Vehicles", "Vehicles")}
{OpsOption("JobSearch", "Job Search")}
{OpsOption("PersonalCompany", "Personal Company")}
{OpsOption("Inves`enter code here`tInIdea", "Invest In Idea")}
</ul>
</div>
</div>
</li>
<li>
<Link to="/contact">Help & Support</Link>
</li>
<li className="has-child language">
{ActiveLanguage(language.iso2, language.speak)}
<div className="wrapper">
<div id="nav-languages" className="nav-wrapper">
<ul>
{LanguageOption("English", "us", setLanguage)}
{LanguageOption("British", "gb", setLanguage)}
{LanguageOption("Latviešu", "lv", setLanguage)}
{LanguageOption("Estonia", "ee", setLanguage)}
{LanguageOption("Lithuania", "lt", setLanguage)}
{LanguageOption("Russian", "ru", setLanguage)}
{LanguageOption("Deutch", "de", setLanguage)}
{LanguageOption("French", "fr", setLanguage)}
</ul>
</div>
</div>
</li>
</ul>
</div>
);
}
function LanguageOption(label, classLabel, setLanguage) {
return (
<li
onClick={(event) => changeTounge(event, setLanguage, label, classLabel)}
data-id={label}
data-value={classLabel}
>
<Link to="#nav-locations">
<span className={"flag-icon flag-icon-" + classLabel}></span>
{" "}
{label}
</Link>
</li>
);
}
function changeTounge(e, setLanguage, label, classLabel) {
console.log("Write something!!");
console.log(e.currentTarget.dataset.id);
console.log(e.currentTarget.dataset.value);
setLanguage({ iso2: classLabel, speak: label });
}
function OpsOption(pageLink, label) {
return (
<li>
<Link to={"/" + pageLink}>{label}</Link>
</li>
);
}
function ActiveLanguage(iso2, activeLanguage) {
return (
<a href="#nav-languages">
<span className={"flag-icon flag-icon-" + iso2}></span> {activeLanguage}
</a>
);
}
export default PrimaryNav;

can't use the Refs properly in react js, Cannot read property of undefined

I am very new to react and js,
I have a menu and submenu, I use a list to map data,
I want to write a function, so onmouseover one item in the list,
if it has submenu, it will show.
the problem is that I can't select the submenu using ref.
It is just too complicated for me, any help would be much appreciated!
enter image description here
import React, { Component } from "react";
export class Menu extends Component {
constructor(props) {
super(props);
this.liRefs = [];
}
showSubmenu = (e) => {
// this.liRefs.current.style.display = "block";
for (var i = 0; i < this.liRefs.length; i++) {
this.liRefs[i].current.style.display = "block";
}
// console.log(this.liRefs[10]);
};
getStyle = (e) => {
e.target.style.background = "red";
};
render() {
return (
<ul className="mainmenu">
{this.props.items.map((i) =>
i.subitems ? (
<li key={i.id} onMouseOver={this.showSubmenu}>
{i.icon}
{i.name}
<ul key={i.id} ref={(ref) => (this.liRefs[i.id] = ref)}>
{i.subitems.map((item) => (
<div key={item.id} className="submenu">
{item.icon}
{item.name}
</div>
))}
</ul>
</li>
) : (
<li key={i.id}>
{i.icon}
{i.name}
{i.img}
</li>
)
)}
</ul>
);
}
}
export default Menu;
You are giving ref value to this. liRefs[i.id] and accessing through this. liRefs[i] so that both are the different change your code as below:
{this.props.items.map((i,index) =>
i.subitems ? (
<li key={i.id} onMouseOver={this.showSubmenu}>
{i.icon}
{i.name}
<ul key={i.id} ref={(ref) => (this.liRefs[i.id] = ref)}>
{i.subitems.map((item) => (
<div key={item.id} className="submenu">
{item.icon}
{item.name}
</div>
))}
</ul>
</li>
) : (
<li key={i.id}>
{i.icon}
{i.name}
{i.img}
</li>
)
)}

react dom traverself or parent div targeting?

I have the following in React (using redux):
....
deleteBase(title) {
this.props.dispatch(removeBase(title));
}
render() {
const baseList = this.props.bases.map(base => {
return (
<li key={base.title} className="base">
<Link to={base.title}>{base.title}</Link>
<i
className="fas fa-times"
onClick={title => this.deleteBase(title)}
/>
</li>
);
});
}
In the dispatch function of removeBase I will need to have access to the single base.title from the clicked element. No matter how I try to get the right title transferred over to the function I can't do it.
return (
<ul className="baseWrapper">
{baseList}
<li className="add-list-wrapper">
<AddBase type="base" onAdd={title => this.addBase(title)} />
</li>
</ul>
);

Resources