Using document.classlist.remove in react - reactjs

When I click on a new li tag, I want the classname to change to active (which works), but also remove active from the rest of the li tags (which doesn't work). document.getElementByTagName('li').classList.remove="active", doesn't work because it is saying it is not defined. Should I go about this a different way... maybe storing something different in the state?
import React, {useState, useEffect} from 'react';
import './Anime.css';
function Anime(){
const [currentCase, setCurrentCase] = useState(0)
function getAnime(){
fetch('https://kitsu.io/api/edge/anime')
.then(response => response.json())
.then(data => console.log(data));
}
function currentSelector(e){
document.getElementsByTagName('li').clasList.remove("active");
setCurrentCase(e.target.value)
e.target.className = "active"
}
useEffect(() => {
getAnime();
}, []);
return(
<div className="anime">
{/* Selectors */}
<ul>
<li value= {0} className="active" onClick={currentSelector}>Trending</li>
<li value={1} onClick={currentSelector}>Action</li>
<li value={2} onClick={currentSelector}>Adventure</li>
<li value={3} onClick={currentSelector}>Comedy</li>
<li value={4} onClick={currentSelector}>Drama</li>
<li value={5} onClick={currentSelector}>Magic</li>
<li value={6} onClick={currentSelector}>Romance</li>
</ul>
</div>
)
}
export default Anime

Don't use the usual DOM API for things like this in React, instead use React's state management and conditional rendering functionality. You already have a state variable to track the active case (currentCase), so you can just set the class name conditionally while rendering.
For each li, just check if the value of currentCase matches the value for that li and if so, give that li the class active, otherwise give a different class.
For example:
import React, {useState, useEffect} from 'react';
import './Anime.css';
function Anime(){
const [currentCase, setCurrentCase] = useState(0)
function getAnime(){
fetch('https://kitsu.io/api/edge/anime')
.then(response => response.json())
.then(data => console.log(data));
}
function currentSelector(e){
setCurrentCase(Number(e.target.value));
}
useEffect(() => {
getAnime();
}, []);
return(
<div className="anime">
{/* Selectors */}
<ul>
<li value={0} className={currentCase === 0 ? "active" : ""} onClick={currentSelector}>
Trending
</li>
<li value={1} className={currentCase === 1 ? "active" : ""} onClick={currentSelector}>
Action
</li>
<li value={2} className={currentCase === 2 ? "active" : ""} onClick={currentSelector}>
Adventure
</li>
<li value={3} className={currentCase === 3 ? "active" : ""} onClick={currentSelector}>
Comedy
</li>
<li value={4} className={currentCase === 4 ? "active" : ""} onClick={currentSelector}>
Drama
</li>
<li value={5} className={currentCase === 5 ? "active" : ""} onClick={currentSelector}>
Magic
</li>
<li value={6} className={currentCase === 6 ? "active" : ""} onClick={currentSelector}>
Romance
</li>
</ul>
</div>
)
}
export default Anime
Or extract the class name logic into a function (defined within your Anime component) and call that function for each li element:
function getLiClassName(value) {
if (value === currentCase) {
return "active";
}
return "";
}
And use like this:
<li value={0} className={getLiClassName(0)} onClick={currentSelector}>
Trending
</li>

Related

Display Data based on category using ReactJS

I have created a restaurant menu with the help of an API. Now I want to display only the data of a particular category using the category name with the help of react hooks.
Can someone guide me about the function I need to implement in my code to achieve the functionality based on my requirements.
This is App.js:
import React, { useState, useEffect } from 'react';
const Menu = () => {
/*Toggle sidemenu start*/
const [toggleState, setToggleState] = useState(1);
const toggleTab = (index) =>{
setToggleState(index);
}
/*Toggle sidemenu end*/
/* API Data start */
const [data, setData] = useState();
useEffect(() => {
async function fetchData() {
try {
const response = await fetch('https://63b040676a74151a1bbcf341.mockapi.io/restaurantmenu')
const data = await response.json();
setData(data);
console.log("data")
} catch (error) {
console.error(error);
}
}
fetchData();
}, []);
/* API Data end */
return (
<div className='main-content'>
<div className='restaurant-card'>
<div className='image'>
<img src='/images/kfc.png'/>
</div>
<div className='restaurant-details'>
<h2>KFC</h2>
<p>Abids, Hyderabad</p>
<p><img src='/images/rating.png'/><span>4.5 Rating | 09:00 A.M - 11:00 P.M</span></p>
</div>
</div>
<div className='item-category'>
<h3>Categories</h3>
<ul>
<li className={toggleState === 1 ? "tabs active" : "tabs"} onClick={()=>toggleTab(1)}>Pizza</li>
<li className={toggleState === 2 ? "tabs active" : "tabs"} onClick={()=>toggleTab(2)}>Bread</li>
<li className={toggleState === 3 ? "tabs active" : "tabs"} onClick={()=>toggleTab(3)}>Shakes</li>
<li className={toggleState === 4 ? "tabs active" : "tabs"} onClick={()=>toggleTab(4)}>Ice Cream</li>
<li className={toggleState === 5 ? "tabs active" : "tabs"} onClick={()=>toggleTab(5)}>Cakes</li>
<li className={toggleState === 6 ? "tabs active" : "tabs"} onClick={()=>toggleTab(6)}>Juices</li>
</ul>
</div>
<div className='item-list'>
<div className="box">
<input type="text" className="search-input" placeholder="Search for dishes"/>
<img src='/images/search.png'/>
</div>
<div className="content">
{
data
?
(
<div>
<div>
{data.map(item =>
<div>
<h2>{item.Category}</h2>
<div className='items' key={item.id}>
<ul>
<li>{item.Type}</li>
<li>{item.Name}</li>
<li>₹ {item.Price}</li>
<div className='hr'></div>
</ul>
</div>
</div>
)}
</div>
</div>
)
:
(<div>Loading...</div>)
}
</div>
</div>
</div>
)
}
export default Menu;
This is the sandbox link for your reference.
https://codesandbox.io/s/fragrant-wave-9b528h?file=/src/App.js
Share me with whatever the idea you have that can work for me.
You have multiples options:
Only fetch the required category with your API (you need to have the hand on it) by passing the required category to your API, like with querystrings such as fetch(https://63b040676a74151a1bbcf341.mockapi.io/restaurantmenu?category=${categoryName}), and in your API if category's querystring is defined, only show required menu related to the required category.
Filter your returned results to only display wanted categories. It's not quite recommended because you will need to wait to fetch ALL your menu, and filter them afterward so it's quite a loss of time. but it can be achieved easily by replacing your:
<div className="content">
{
data
?
by:
<div className="content">
{data?.filter(({ Category }) => Category === "WantedCategoryName")
?

to unique useState variable for repeated item in ReactJS

I am newbie in reactjs. How can set a variable for every single iterating items?
I have some list and when click trash icon i want to show loading icon just for clicked item
const [deleteLoading, setDeleteLoading] = useState(false);
<ul className="mt-2">
{projects && projects.map((project, index) => (
<li key={index}>
<NavLink to={`someurl`}>{project.project_name}</NavLink>
<span className={`${deleteLoading ? "" : "hidden"}`}>
<svg> ..loading icon.. </svg>
</span>
<span onClick={() => deleteProject(project._id)} className={`${deleteLoading ? "hidden" : ""}`}>
<svg> ..trash icon.. </svg>
</span>
</li>)
)}
</ul>
when i clicked trash button it seems like
const deleteProject = async (id) => {
setDeleteLoading(true)
// some deleting code..
}
The behaviour you have is normal here because you use the same logic for every elements in your map function : ${deleteLoading ? "" : "hidden"}
If you want to have the delete icon only in the good project you should instead define a state var like deletingProject which contain the ID or the name of the deleting project.
Then you just have to modify the loading icon span like this :
<span className={`${project.project_id === deletingProject ? "" : "hidden"}`}>
I would suggest creating a component to hold each project, and add the delete functionality to the individual component. For example:
Project.js
export default function Project({ project, index }) {
const [deleteLoading, setDeleteLoading] = useState(false);
return (
<li key={index}>
<NavLink to={`someurl`}>{project.project_name}</NavLink>
<span className={`${deleteLoading ? "" : "hidden"}`}>
<svg> ..loading icon.. </svg>
</span>
<span onClick={() => deleteProject(project._id)} className={`${deleteLoading ? "hidden" : ""}`}>
<svg> ..trash icon.. </svg>
</span>
</li>
}
}
And then import into the Parent component
import Project from './Project.js'
export default function Parent(props) {
return (
<ul className="mt-2">
{projects && projects.map((project, index) => (
<Project project={project} index={index}/>
)}
</ul>
)
)}
I hope that helps
If you want to keep each project logic and state separate I'd suggest to create another component and host any project related logic there.
An example
function ProjectItem({ name }) {
const [deleteLoading, setDeleteLoading] = useState(false)
function handleDelete() {
// Some logic here
setDeleteLoading(true)
}
return (
<li>
<NavLink to={`someurl`}>{name}</NavLink>
<span className={`${deleteLoading ? "" : "hidden"}`}>
<svg> ..loading icon.. </svg>
</span>
<span
onClick={handleDelete}
className={`${deleteLoading ? "hidden" : ""}`}
>
<svg> ..trash icon.. </svg>
</span>
</li>
)
}
function MainComponent() {
return (
<ul className="mt-2">
{projects &&
projects.map((project) => (
<ProjectItem key={project._id} name={project.project_name} />
))}
</ul>
)
}
NOTES: Never use index as key prop in React Components. If you have an id already then use that as it's key value. Key prop is intended to be unique among all your rendered components
If you have items that can have it's own state move them to a new component. Hope you end up liking React and learning a lot my man ;3

Hiding an element after a while in Next.js

I have a header component as a function component. I want show a popup when logo text is clicked. After for a time it should close automatically. I use hooks for state of popup. But set state function doesn't work in setTimeout function. How can fix this?
import Link from 'next/link'
import style from './header.module.css'
const Header = () => {
const [popupOpen, setPopupOpen] = React.useState(false)
return (
<header className={style.header}>
<nav className={style.nav}>
<div
className={style.popupContainer}
onClick={() => {
setPopupOpen(!popupOpen)
console.log(popupOpen)
setTimeout(() => {
console.log(popupOpen)
setPopupOpen(!popupOpen)
console.log(popupOpen)
}, 1000)
}}
>
<span className={style.logo}>Logo</span>
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
</div>
<ul className={style.ul}>
<li>
<Link href='/'>
<a>.home</a>
</Link>
</li>
<li>
<Link href='/contact'>
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
)
}
export default Header
Console log:
Let me suggests, this is the same question as:
React - useState - why setTimeout function does not have latest state value?
const _onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
setPopupOpen(popupOpen => !popupOpen)
}, 2000);
};
Its happening because setPopupOpen is asynchronous. So by the time setPopupOpen(!popupOpen) is called it has same value as onClick first setPopupOpen(!popupOpen) so eventually when it called both setPopup doing same state update i.e both updating as false. Better way is to usesetPopupOpen callback function to update the value. I added this code.
import { useState } from "react";
import Link from "next/link";
import style from "./style.module.css";
const Header = () => {
const [popupOpen, setPopupOpen] = useState(false);
const toggle = () => {
setPopupOpen((prev) => !prev);
};
const onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
toggle();
}, 1000);
};
return (
<header className={style.header}>
<nav className={style.nav}>
<div className={style.popupContainer} onClick={onClick}>
<span className={style.logo}>Logo</span>
{popupOpen && (
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
)}
</div>
<ul className={style.ul}>
<li>
<Link href="/">
<a>.home</a>
</Link>
</li>
<li>
<Link href="/contact">
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
);
};
export default function IndexPage() {
return (
<div>
<Header />
</div>
);
}
Here is the demo: https://codesandbox.io/s/pedantic-haibt-iqecz?file=/pages/index.js:0-1212

REACT JS How to show one component and hide the others on click

i need your help because i can't make a single page website with many components who are hide and show. I have begin with this code but when I click on all link, all of the components is showing at the same time. I would show one component when I click on "portfolio" and hide the others. And if I click on "Offres" the "portfolio" is hidden like the others for show "Offres".
Thank you so much and sorry for my english.
import './style.css'
import logo_studio from './assets/logo_studio.png'
import Main from './components/Main'
import Portfolio from './components/Portfolio'
import Offres from './components/Offres'
import Contact from './components/Contact'
import Apropos from './components/Apropos'
class App extends Component {
constructor () {
super()
this.state = {
name: 'React',
showHideDemo1: false,
showHideDemo2: false,
showHideDemo3: false,
showHideDemo4: false
}
this.hideComponent = this.hideComponent.bind(this)
}
hideComponent (name) {
switch (name) {
case 'showHideDemo1':
this.setState({ showHideDemo1: !this.state.showHideDemo1 })
break
case 'showHideDemo2':
this.setState({ showHideDemo2: !this.state.showHideDemo2 })
break
case 'showHideDemo3':
this.setState({ showHideDemo3: !this.state.showHideDemo3 })
break
case 'showHideDemo4':
this.setState({ showHideDemo4: !this.state.showHideDemo4 })
break
default:
return <Main />
}
}
render () {
const { showHideDemo1, showHideDemo2, showHideDemo3, showHideDemo4 } = this.state
return (
<div className='section'>
<img src={logo_studio} class='logo1' alt='' />
<div className='connect sweep-to-right'>
<div>Espace client</div>
<i className='fas fa-user-circle userLogo' />
</div>
<div className='menu1'>
<ul>
<li className='hvr-float underline-from-left' onClick={() => this.hideComponent('showHideDemo1')}>Portfolio</li>
<li className='hvr-float underline-from-left' onClick={() => this.hideComponent('showHideDemo2')}>Offres</li>
<li className='hvr-float underline-from-left' onClick={() => this.hideComponent('showHideDemo3')}>A propos</li>
<li className='hvr-float underline-from-left' onClick={() => this.hideComponent('showHideDemo4')}>Contact</li>
</ul>
</div>
{showHideDemo1 && <Portfolio />}
{showHideDemo2 && <Offres />}
{showHideDemo3 && <Apropos />}
{showHideDemo4 && <Contact />}
</div>
)
}
}
export default App```
You have to use the Router library for this task react-router-dom. You can manage this small scenario by changing your code. As you have to show only one component at a time, you can manage this by only one state variable let say showComponentCount
Change your code by given code
import './style.css'
import logo_studio from './assets/logo_studio.png'
import Main from './components/Main'
import Portfolio from './components/Portfolio'
import Offres from './components/Offres'
import Contact from './components/Contact'
import Apropos from './components/Apropos'
class App extends Component {
constructor () {
super()
this.state = {
name: 'React',
showComponentCount: 0
}
this.showComponent = this.showComponent.bind(this)
}
showComponent (count) {
this.setState({ showComponentCount: count })
}
render () {
const { showComponentCount } = this.state
return (
<div className='section'>
<img src={logo_studio} class='logo1' alt='' />
<div className='connect sweep-to-right'>
<div>Espace client</div>
<i className='fas fa-user-circle userLogo' />
</div>
<div className='menu1'>
<ul>
<li className='hvr-float underline-from-left' onClick={() => this.showComponent(0)}>Portfolio</li>
<li className='hvr-float underline-from-left' onClick={() => this.showComponent(1)}>Offres</li>
<li className='hvr-float underline-from-left' onClick={() => this.showComponent(2)}>A propos</li>
<li className='hvr-float underline-from-left' onClick={() => this.showComponent(3)}>Contact</li>
</ul>
</div>
{showComponentCount == 0 ? <Portfolio /> : null}
{showComponentCount == 1 ? <Offres /> : null}
{showComponentCount == 2 ?<Apropos /> : null}
{showComponentCount == 3 ? <Contact /> : null}
</div>
)
}
}
export default App
In this code, I have changed hideComponent to showComponent as We have to show only one component at a time.
We just assign each component a number so that we can check it for rendering a component.
I hope this will work for you.
You should use one value to keep a track of what is opened and display it. I am using showItem to keep a track of what is open.
import './style.css'
import logo_studio from './assets/logo_studio.png'
import Main from './components/Main'
import Portfolio from './components/Portfolio'
import Offres from './components/Offres'
import Contact from './components/Contact'
import Apropos from './components/Apropos'
class App extends Component {
constructor () {
super()
this.state = {
name: 'React',
showItem: ''
}
this.hideComponent = this.hideComponent.bind(this)
}
hideComponent (name) {
switch (name) {
case 'showHideDemo1':
this.setState({ showItem: this.state.showItem !== 'showHideDemo1' ? 'showHideDemo1' : '' })
break
case 'showHideDemo2':
this.setState({ showItem: this.state.showItem !== 'showHideDemo2' ? 'showHideDemo2' : '' })
break
case 'showHideDemo3':
this.setState({ showItem: this.state.showItem !== 'showHideDemo3' ? 'showHideDemo3' : '' })
break
case 'showHideDemo4':
this.setState({ showItem: this.state.showItem !== 'showHideDemo4' ? 'showHideDemo4' : '' })
break
default:
return <Main />
}
}
render () {
const { showItem } = this.state
return (
<div className='section'>
<img src={logo_studio} class='logo1' alt='' />
<div className='connect sweep-to-right'>
<div>Espace client</div>
<i className='fas fa-user-circle userLogo' />
</div>
<div className='menu1'>
<ul>
<li className='hvr-float underline-from-left' onClick={() => this.hideComponent('showHideDemo1')}>Portfolio</li>
<li className='hvr-float underline-from-left' onClick={() => this.hideComponent('showHideDemo2')}>Offres</li>
<li className='hvr-float underline-from-left' onClick={() => this.hideComponent('showHideDemo3')}>A propos</li>
<li className='hvr-float underline-from-left' onClick={() => this.hideComponent('showHideDemo4')}>Contact</li>
</ul>
</div>
{showItem === 'showHideDemo1' && <Portfolio />}
{showItem === 'showHideDemo2' && <Offres />}
{showItem === 'showHideDemo3' && <Apropos />}
{showItem === 'showHideDemo4' && <Contact />}
</div>
)
}
}
export default App```

Adding a CSS class to a JSX in ReactJS

I wanted to add an 'active' class to a menu element, written in ReactJS. I tried doing it with the conventional JS method, but it failed. A click on any <li> tag, should result is removal of the 'active' class from all the <li>, and retain/ add it only to the one list tag in which the click was triggered.
Note: I know it may seem very naive on my part, but I'm just starting with ReactJS. Please ignore the stupidity.
import React, { Component } from 'react';
class Sidebar extends Component{
render(){
return(
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li className="mb-3 pl-2 menuTitle active" id="MenuTitle1">
...
</li>
<li className="mb-3 pl-2 menuTitle" id="MenuTitle2" onClick={this.clickMenu.bind(this,'MenuTitle2')}>
...
</li>
<li className="mb-3 pl-2 menuTitle" id="MenuTitle3" onClick={this.clickMenu.bind(this,'MenuTitle3')}>
...
</li>
</ul>
</div>
</div>
);
}
clickMenu(id){
// Add class 'active' on the clicked <li>, and remove from all other <li>
}
}
export default Sidebar;
I saw a similar question here, but that couldn't help me.
Idea is, store the id of clicked item in state variable and put the check with className. If item's id is same as state value then only assign the className active.
Write it like this:
class Sidebar extends Component{
constructor() {
super()
this.state = {
activeItem: 'MenuTitle1'
}
}
clickMenu(id){
// Add class 'active' on the clicked <li>, and remove from all other <li>
this.setState({
activeItem: id,
})
}
getClassName(id) {
if(id === this.state.activeItem) return 'mb-3 pl-2 menuTitle active'
return 'mb-3 pl-2 menuTitle'
}
render(){
return(
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li
id="MenuTitle1"
className={this.getClassName('MenuTitle1')}
onClick={this.clickMenu.bind(this,'MenuTitle1')}>
...
</li>
<li
id="MenuTitle2"
className={this.getClassName('MenuTitle2')}
onClick={this.clickMenu.bind(this,'MenuTitle2')}>
...
</li>
<li
id="MenuTitle3"
className={this.getClassName('MenuTitle3')}
onClick={this.clickMenu.bind(this,'MenuTitle3')}>
...
</li>
</ul>
</div>
</div>
);
}
}
You can maintain the state for clicked menu item:
clickMenu(id){
this.setState({activeMenu: id})
}
Then, define className like this:
className={
this.state.activeMenu == id {/* eg. "MenuTitle1" */}
? 'mb-3 pl-2 menuTitle active'
: 'mb-3 pl-2 menuTitle'
}
Like Bhojendra suggested store datas linked to your display inside your state then when you want to update the display of your component use the method setState, this will trigger render again (react style).
import React, { Component } from 'react';
import ReactDOM from "react-dom";
class Sidebar extends Component {
constructor() {
super();
this.state = {
activeMenuId: "MenuTitle1"
};
}
render() {
return (
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li className={`mb-3 pl-2 menuTitle ${this.state.activeMenuId === "MenuTitle1" ? "active" : ""}`} id="MenuTitle1" onClick={this.clickMenu.bind(this, 'MenuTitle1')}>
1
</li>
<li className={`mb-3 pl-2 menuTitle ${this.state.activeMenuId === "MenuTitle2" ? "active" : ""}`} id="MenuTitle2" onClick={this.clickMenu.bind(this, 'MenuTitle2')}>
2
</li>
<li className={`mb-3 pl-2 menuTitle ${this.state.activeMenuId === "MenuTitle3" ? "active" : ""}`} id="MenuTitle3" onClick={this.clickMenu.bind(this, 'MenuTitle3')}>
3
</li>
</ul>
</div>
</div>
);
}
clickMenu(id) {
// Add class 'active' on the clicked <li>, and remove from all other <li>
this.setState({activeMenuId: id});
}
}
export default Sidebar;
ReactDOM.render(<Sidebar />, document.body);
Another way of just make using initialstate and setState.
import React, { Component } from "react";
class Sidebar extends Component {
constructor(props) {
super(props);
this.initialState = {
MenuTitle1: "active",
MenuTitle2: "",
MenuTitle3: ""
};
this.state = this.initialState;
}
render() {
return (
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li
className={`mb-3 pl-2 menuTitle ${this.state.MenuTitle1} `}
id="MenuTitle1"
onClick={this.clickMenu.bind(this, "MenuTitle1")}
>
one
</li>
<li
className={`mb-3 pl-2 menuTitle ${this.state.MenuTitle2}`}
id="MenuTitle2"
onClick={this.clickMenu.bind(this, "MenuTitle2")}
>
two
</li>
<li
className={`mb-3 pl-2 menuTitle ${this.state.MenuTitle3}`}
id="MenuTitle3"
onClick={this.clickMenu.bind(this, "MenuTitle3")}
>
three
</li>
</ul>
</div>
</div>
);
}
clickMenu(id) {
this.setState(this.initialState);
this.setState({
[id]: "active"
});
}
}
export default Sidebar;

Resources