Add a class and remove class from another in ReactJS - reactjs

I want to create a tab using ul and li and the tab will be shown the same content which is selected and should be show a button which is send me to the next tab and that will be active for indication which tab is showing to me
render() {
return (
<div>
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</li>
</ul>
<div id="tab-menu-1">Tab 1 Content <button>Move to Tab 2</button></div>
//in default it should show the tab 1 content only and When i Press this button on above Div it should be moved to next tab and the class name will be transfer to the tab 2 on <li>
<div id="tab-menu-2">Tab 2 Content <button>Move to Tab 2</button></div>
//in default it should show the tab 2 content only and When i Press this button on above Div it should be moved to next tab and the class name will be transfer to the tab 3 on <li>
<div id="tab-menu-3">Tab 3 Content <button>Move to Tab 2</button></div>
//in default it should show the tab 3 content only and When i Press this button on above Div it should be moved to next tab and the class name will be transfer to the tab 1 on <li>
</div>
);
}

You can save on the state of the component the activeIndex id, and render conditionally the className.
class Demo extends React.Component {
state = {
activeIdx: 0
};
render() {
return (
<div>
<ul>
<li><a href="#tab-menu-1" className={this.state.activeIdx === 0 ? 'active' : ''} data-index={0} onClick={this.handleChange}>Tab 1</a>
</li>
<li><a href="#tab-menu-2" className={this.state.activeIdx === 1 ? 'active' : ''} data-index={0} onClick={this.handleChange}>Tab 2</a></li>
<li><a href="#tab-menu-3" className={this.state.activeIdx === 2 ? 'active' : ''} data-index={0} onClick={this.handleChange}>Tab 3</a></li>
</ul>
<div id="tab-menu-1">Tab 1 Content <button>Move to Tab 2</button></div>
<div id="tab-menu-2">Tab 2 Content <button>Move to Tab 2</button></div>
<div id="tab-menu-3">Tab 3 Content <button>Move to Tab 2</button></div>
</div>
);
}
handleChange = (evnt) => {
this.setState({activeIdx: evnt.target.data.index});
}
}
A better approach would be to generate the list of items out of an array.
class Demo extends React.Component {
state = {
activeIdx: 0,
list: ["content 1", "content 2", "content 3"]
};
render() {
return (
<div>
<ul>
{this.state.list.map((item, index) => (
<li key={item}>
<a
href={`#tab-menu-${index + 1}`}
className={this.state.activeIdx === index && "active"}
data-index={index}
onClick={this.handleChange}
>
Tab {index + 1}
</a>
</li>
))}
</ul>
{this.state.list.map((item, index) => (
<div id={`tab-menu-${index + 1}`} key={item}>
{item}
<button>Move to Tab {index + 2}</button>
</div>
))}
</div>
);
}
handleChange = evnt => {
this.setState({ activeIdx: evnt.target.data.index });
};
}

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>
</>
);
}

Why my useState doesn't work as it should?

So I have a sidebar in my react app, and when the screen is mobile, it goes to the top and only the button "menu" remains, and when clicked it should display the sidebar.
Example is below:
Sidebar:
Sidebar with mobile screen(with clicked button):
So I have a code like this:
const Sidebar = () => {
const [sideBar, setSidebar] = useState(false);
return (
<div className="sidebar">
<span class="btn">Menu</span>
<div className="profile">
<span>Alim Budaev</span>
<span>Available for work</span>
</div>
<ul className="sidebarlist" id={setSidebar ? "hidden" : ""}>
{SlidebarData.map((val,key) =>{
return (
<li
className="row"
id={window.location.pathname == val.link ? "active" : ""}
key={key}
onClick={()=> {
window.location.pathname = val.link
}}>
{""}
<div>
{val.title}
</div>
</li>
);
})}
</ul>
</div>
);
}
export default Sidebar;
So as you could see, I'm using useState(), so when state is true, the "hidden" id should be added to ul and it should display, but state is false in my code, and it display anyway:
I'm not sure what could be the problem, but I think if state is false, the "hidden" id shouldn't be added, and the sidebar shouldn't be displayed.

onClick triggering all sub menus instead of only the clicked one

When I click on an item it should expend some sub items. This is working but if I have two, three or four etc. list items then when I click on one it expands ALL of the sub items for all the list items which is obviously not what I want. How can I fix this code to make it only open expand the one I actually clicked on?
const [sideActive, setSideActive] = useState(false);
const toggleSideActive = () => {
setSideActive(!sideActive);
};
html:
<li>
<div
onClick={toggleSideActive}
className={
sideActive
? `${styles.navList__subheading} row ${styles.row__align_v_center} ${styles.navList__subheading__open}`
: `${styles.navList__subheading} row ${styles.row__align_v_center}`
}
>
<span className={styles.navList__subheading_icon}>
<FaBriefcaseMedical />
</span>
<span className={styles.navList__subheading_title}>
insurance
</span>
</div>
<ul
className={
sideActive
? `${styles.subList}`
: `${styles.subList} ${styles.subList__hidden}`
}
>
<li className={styles.subList__item}>medical</li>
<li className={styles.subList__item}>medical</li>
<li className={styles.subList__item}>medical</li>
</ul>
</li>
You can create a local state for tracking the selected id and show the content based on the state. Also, update the selected Id on click of the tab like below.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [selected, setSelected] = useState("");
const data = [
{
id: 1001,
name: "Tab - 1",
content: ["test1", "test2"]
},
{
id: 1002,
name: "Tab - 2",
content: ["test21", "test22"]
}
];
return (
<div className="App">
<ul class="parent">
{data.map((v) => (
<li onClick={() => setSelected(selected !== v.id ? v.id : "")}>
{v.name}
{selected === v.id && (
<ul class="content">
{v.content.map((val) => (
<li>{val}</li>
))}
</ul>
)}
</li>
))}
</ul>
</div>
);
}
For the below example, click on the tab to see the content.
Working code - https://codesandbox.io/s/long-leaf-cl4cy?file=/src/App.js:0-759
Let me know if you are facing any issues.

One component in every view but with different className

So, I have a progress bar for my application that I have to put it in every other component with different class name to change the color and show the progress of the process.
<ol>
<li className="complete" data-step="1">
</li>
<li className="active" data-step="2">
</li>
<li data-step="3">
</li>
</ol>
Is there a more proper way to prevent copying the bar in every component and just change the class names based on the component it is in?
You can simply make conditional rendering
Example:
<ol>
<li className={condition ? "active" : "complete"} data-step="1">
</li>
<li className={condition ? "active" : "complete"} data-step="2">
</li>
<li data-step="3">
</li>
</ol>
I would do something like this:
const completedStep = 2; // 1, 2, 3 (0 == nothing is completed)
const steps = ['Step 1', 'Step 2', 'Step 3']; // can be whole component, not just a string
return (
<ol>
{steps.map((step, index) => {
return (
let className;
if (index < completedStep) {
className = 'completed';
} else if (index === completedStep) {
className = 'active';
} else {
className = '';
}
<li
key={index}
data-step={index + 1}
className={className}
>
{step}
</li>
);
})}
</ol>
);
The parameters above can be passed down as props to the component.

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