React JS Toogle Class - reactjs

I have a project that I am developing with React JS. The problem is that I have a button and when I click on it, I just want the icon on the button I click to change. But the icons on all the buttons I click change. My code is below.
constructor(props){
super(props)
this.state={
icon: false
}
}
active = (event) => {
this.setState({icon: !this.state.icon})
}
.....
const menu = ['A','B','C','A','B','C','A','B','C']
<div className="nav_menu">
<ul>
{menu.map((item,index) =>
<li key = {index}>
<Link data-id = {index} className="inactive" to={`${match.url}`} onClick={this.active}>
<span>
<span>
<FontAwesomeIcon icon={faHome} className="icon"/>
</span>
{item}
</span>
<FontAwesomeIcon data-id = {index} icon={icon ? faAngleDown:faAngleRight} className="angle"/>
</Link>
</li>
)}
</ul>
How do I fix this?

Having just one variable wouldn't suffice as you are not storing which button index has been clicked to accurately show the icon on only that button.
constructor(props){
super(props)
this.state={
icon: false,
clickedIndex: -1,
}
}
active = (clickedIndex)=> (event) => {
this.setState(prevState => ({icon: !prevState.icon, clickedIndex }));
}
.....
const menu = ['A','B','C','A','B','C','A','B','C']
<div className="nav_menu">
<ul>
{menu.map((item,index) =>
<li key = {index}>
<Link data-id = {index} className="inactive" to={`${match.url}`} onClick={this.active(index)}>
<span>
<span>
<FontAwesomeIcon icon={faHome} className="icon"/>
</span>
{item}
</span>
<FontAwesomeIcon data-id = {index} icon={(icon && index === this.state.clickedIndex) ? faAngleDown:faAngleRight} className="angle"/>
</Link>
</li>
)}
</ul>

Sample how you can select particular. live demo https://codesandbox.io/s/focused-browser-fls2w
export default class Abc extends Component {
constructor(props) {
super(props);
this.state = { icon: false };
}
active = item => {
this.setState({ icon: item });
};
render() {
const menu = ["A", "B", "C", "A", "B", "C", "A", "B", "C"];
return (
<div className="nav_menu">
<ul>
{menu.map((item, index) => (
<li
key={index}
onClick={() => this.active(index)}
style={{ color: this.state.icon === index ? "red" : "" }}
>
{item}
</li>
))}
</ul>
</div>
);
}
}

Related

Change state of another component in React

As of now, I have a class generating all the list for a component that will be called multiple times. However is it possible for the user to only select one item with the same ID?
class Skills extends Component {
constructor(props) {
super(props);
this.img = props.src
this.name = props.name
}
render() {
return (
<React.Fragment>
<div>
<Row>
<Image src={this.img} style={skillIcon} rounded />
<div>
<h4>{this.name}</h4>
<ul className="sul">
<li id="lvl 1">1</li>
<li id="lvl 2">2</li>
<li id="lvl 3">3</li>
<li id="lvl 4">4</li>
<li id="lvl 5">5</li>
<li id="lvl 6">6</li>
<li id="lvl 7">7</li>
<li id="lvl 8">8</li>
<li id="lvl 9">9</li>
<li id="lvl 10">10</li>
<li id="lvl 11">11</li>
<li id="lvl 12">12</li>
<li id="lvl 13">13</li>
<li id="lvl 14">14</li>
<li id="lvl 15">15</li>
</ul>
</div>
</Row>
</div>
</React.Fragment>
);
}
}
For example, if li with id lvl 1 is selected, the next time someone clicks on another lvl 1 li of the same regenerated component, the previous selection would be deselected.
You need to store user selection in state and update state when user selected another item.
State can be handled in two ways.
Classic Class components way
// its better to have a array with levels
const levels = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
class Skills extends Component {
constructor(props) {
super(props);
this.state = {
selection:null
}
this.img = props.src
this.name = props.name
}
render() {
const {selection} = this.state
return (
<React.Fragment>
<div>
<Row>
<Image src={this.img} style={skillIcon} rounded />
<div>
<h4>{this.name}</h4>
<ul className="sul">
{levels.map((level) => {
const isSelected = selection === level;
return (
<li
id={`lvl ${level}`}
key={level}
onClick={() => setSelection(level)}
style={{
padding: isSelected ? 10 : 2,
backgroundColor: isSelected ? 'red' : 'white',
}}
>
{level}
</li>
);
})}
</ul>
</div>
</Row>
</div>
</React.Fragment>
);
}
}
new functional hooks way
function Skills() {
const [selection, setSelection] = useState(null);
return (
<>
<div>
<img src={img} />
<div>
<h4>{name}</h4>
<ul className="sul">
{levels.map((level) => {
const isSelected = selection === level;
return (
<li
id={`lvl ${level}`}
key={level}
onClick={() => setSelection(level)}
style={{
padding: isSelected ? 10 : 2,
backgroundColor: isSelected ? 'red' : 'white',
}}
>
{level}
</li>
);
})}
</ul>
</div>
</div>
</>
);
}
Not related but i just wanna suggest
1. If src is not dynamic , just assign a const variable assigned like
const LOGO_URL = "url to logo"
const logoStyle = {
...some css
}
<Image src={LOGO_URL} style={logoStyle} />
2. If this array of level numbers is dynamic (some api or something) , get it from props
function Skill(props){
const { levels } = props
return (
<>
{levels.map(i => {
...do anything
})}
</>
)
}
update: when you need to update parent State from child component
function RootComponent() {
const [rootState,setRootState] = useState(null)
return (
<SkillsOrAnyComponent updateState={setRootState} />
)
function SkillsOrAnyComponent(props){
const { updateState } = props
const changeMainState = (val) => {
updateState(val)
}
return ...some jsx
}
}

Trying to toggle between icon to text on my navigation using React

I have some code down and it works but my problem is that it changes all of the icons at the same time when i scroll over just one. I only want the icon that I hover over to change, so any help would be appreciated.
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {isHovered: false};
this.toggleHover = this.toggleHover.bind(this);
}
toggleHover() {
this.setState(prevState => ({isHovered: !prevState.isHovered}));
}
render() {
return (
<section className="info-section">
<div className="logo">
MATT
</div>
<div className="info-box">
<ul className="nav-links">
<li onMouseEnter={this.toggleHover} onMouseLeave={this.toggleHover}>
{this.state.isHovered
? <a className="home active" href="/">Home</a>
: <a className="home active" href="/"><FontAwesomeIcon icon={faHome} /></a>
}
</li>
<li onMouseEnter={this.toggleHover} onMouseLeave={this.toggleHover}>
{this.state.isHovered
? About
: <FontAwesomeIcon icon={faUser} />
}
</li>
Maybe you can try to use CSS.
<ul className="nav-links">
<li>
<a href="/">
<span>Home</span>
<FontAwesomeIcon icon={faHome} />
</a>
</li>
<li>
<a href="/about">
<span>Home</span>
<FontAwesomeIcon icon={faUser} />
</a>
</li>
...
a {
span {
display: none;
}
&:hover {
span {
display: block;
}
/* hide the icon base on their html tag or class */
i {
display: none;
}
}
}
If you really want to handle this through Javascript (which I'm not suggesting), you can try this.
class App extends Component {
state = {
hover: ""
};
handleMouseEnter = key => {
this.setState({ hover: key });
};
handleMouseLeave = () => {
this.setState({ hover: "" });
};
render() {
const { hover } = this.state;
return (
<ul>
<li
onMouseEnter={() => this.handleMouseEnter("home")}
onMouseLeave={this.handleMouseLeave}
>
<a href="/">
{hover === "home" ? <span>Hover Home</span> : <span>Home</span>}
</a>
</li>
<li
onMouseEnter={() => this.handleMouseEnter("about")}
onMouseLeave={this.handleMouseLeave}
>
<a href="/about">
{hover === "about" ? <span>Hover about</span> : <span>about</span>}
</a>
</li>
</ul>
);
}
}

Hide all div and show one div on clicking multiple button

I am trying to fit 3 component in a single page by hiding/showing on a div.But I am not really getting into how to do it.This is the first div.
<div>
<p>What is the type of your property?</p>
<button >Residence</button>
<button>Commercial</button>
<span style={{background:'transparent', border:'0', fontSize:'16px',color:'#ef3530'}}>Back</span>
<span style={{background:'transparent', border:'0', fontSize:'16px',color:'#ef3530'}}>Next</span>
</div>
Only If i click the 'Commercial' or 'Next' button it would go into the second div and first div will hide.
<div>
<p>What is the type of your commercial property?</p>
<button>Office</button>
<button>Restaurant</button>
<button >Outlet</button>
<span style={{background:'transparent', border:'0', fontSize:'16px',color:'#ef3530'}}>Back</span>
<span style={{background:'transparent', border:'0', fontSize:'16px',color:'#ef3530'}}>Next</span>
</div>
and lastly if i click 'restaurant' button from the first div and any button of the second div except the back button it will go into the third div and other div will hide.this is the third div.
<div>
<div className='slider' style={{ marginTop:'165px',marginLeft:'319px',width:'700px',backgroundColor:'EF5350'}} >
<Slider min={850} max={5000} value={value} onChangeStart={this.handleChangeStart}
onChange={this.handleChange}
onChangeComplete={this.handleChangeComplete}
/>
<div className='value'>{value} Squarefeet</div>
<div style={{marginTop:'86px'}}>
<span onChange={this.handleChange} onClick={() => this.saveValue()} >Next</span>
<span onChange={this.handleChange} onClick={() => this.saveValue()} >Next</span>
</div>
</div>
</div>
I tried to do it this way. But it will not work.
import React from 'react';
import Link from "next/link";
class Jh extends React.Component {
constructor() {
super();
this.state = {
shown: true,
hide: false
};
}
toggle() {
this.setState({
shown: !this.state.shown
});
}
toggles() {
this.setState({
shown: !this.state.hide
});
}
render() {
var shown = {
display: this.state.shown ? "block" : "none"
};
var hidden = {
display: this.state.shown ? "none" : "block"
}
return (
<div>
<button onClick={this.toggle.bind(this)} style={ shown }>
<div>
<p>What is the type of your property?</p>
<button >Residence</button>
<button>Commercial</button>
<span style={{background:'transparent', border:'0', fontSize:'16px',color:'#ef3530'}}>Back</span>
<span style={{background:'transparent', border:'0', fontSize:'16px',color:'#ef3530'}}>Next</span>
</div>
</button>
<button onClick={this.toggles.bind(this)} style={ hidden }>
<div>
<p>What is the type of your commercial property?</p>
<button>Office</button>
<button>Restaurant</button>
<button >Outlet</button>
<span style={{background:'transparent', border:'0', fontSize:'16px',color:'#ef3530'}}>Back</span>
<span style={{background:'transparent', border:'0', fontSize:'16px',color:'#ef3530'}}>Next</span>
</div>
</button>
</div>
)
}
}
export default Jh
What should be my approach?
There are many patterns to achieve a "switch case", I'll try to show my favorites:
For sipmlicity, I'll use a generic use case.
Straight Forward
Managing visible state for every component:
return {visible && <CoolComponent id={1} />};
Switch case in disguise
Manage a state of object keys. (currentCounter)
const countersPicker = {
counter1: <Counter id={1} />,
counter2: <Counter id={2} />,
coolComponent: <CoolComponent id={3} />
};
return {countersPicker[currentCounter]};
Here you also can take action on the object, for example, adding a header:
return {Object.entries(countersPicker).map(([key,component]) =>
<div key={key}>
<h1>Component key = {key}</h1>
{component}
</div>
)};
Filter Children
Manage a predicate and use it for filtering/mapping the children.
Check React.Children API.
return (
<FilterComponents predicate={predicate}>
<Counter key={1} id={1} />
<Counter key={2} id={2} />
<CoolComponent key={3} id={3} />
<BestComponent key={4} id={4} />
</FilterComponents>
);
function FilterComponents({ children, predicate }) {
const filteredChildren = React.Children.toArray(children).filter(child =>
// Use the predicate.
// Filter a child by key, key & type or even use ref etc.
);
return <div>{filteredChildren}</div>;
}
I believe you are looking for something like this.
Main things to-do:
Enhance your state-value. Keep track of the different pages in sequence by using an array. Track the current page. Track the start and end of the collection.
Here is the sandbox as well: https://codesandbox.io/s/unruffled-sun-gpzx6
import React from "react";
class Pages extends React.Component {
state = {
currentPage: "property",
pages: ["property", "type", "firstBusiness"],
start: true,
end: false
};
changePage = event => {
const { currentPage, pages } = this.state;
const { name } = event.target;
//check if we are going to end
if (
name == "next" &&
pages[pages.indexOf(currentPage) + 1] === pages[pages.length - 1]
) {
this.setState({
currentPage: pages[pages.indexOf(currentPage) + 1],
end: true,
start: false
});
//go to next page
} else if (name == "next") {
this.setState({
currentPage: pages[pages.indexOf(currentPage) + 1],
start: false
});
//check if we are going to beginning
} else if (
name == "back" &&
currentPage !== pages[0] &&
pages[pages.indexOf(currentPage) - 1] == pages[0]
) {
this.setState({
currentPage: pages[pages.indexOf(currentPage) - 1],
start: true
});
//go back one page
} else {
this.setState({
currentPage: pages[pages.indexOf(currentPage) - 1],
end: false
});
}
};
goToNextPage = () => {
const { currentPage, pages, end } = this.state;
//check if we are going to end
if (pages[pages.indexOf(currentPage) + 1] === pages[pages.length - 1]) {
this.setState({
currentPage: pages[pages.indexOf(currentPage) + 1],
end: true,
start: false
});
//go to next page
} else if (end) {
return;
} else {
this.setState({
currentPage: pages[pages.indexOf(currentPage) + 1],
start: false
});
}
};
render() {
const { currentPage, start, end } = this.state;
return (
<div style={{ background: "gray" }}>
{currentPage === "property" ? (
<div>
<p>What is the type of your property?</p>
<button onClick={this.goToNextPage}>Residence</button>
<button onClick={this.goToNextPage}>Commercial</button>
</div>
) : null}
{currentPage === "type" ? (
<div>
<p>What is the type of your commercial property?</p>
<button onClick={this.goToNextPage}>Office</button>
<button onClick={this.goToNextPage}>Restaurant</button>
<button onClick={this.goToNextPage}>Outlet</button>
</div>
) : null}
{currentPage === "firstBusiness" ? (
<div>
<p>Is this your first business?</p>
<button onClick={this.goToNextPage}>Yes</button>
<button onClick={this.goToNextPage}>No</button>
</div>
) : null}
<div>
<button onClick={this.changePage} name="back" disabled={start}>
Back
</button>
<button onClick={this.changePage} name="next" disabled={end}>
Next
</button>
</div>
</div>
);
}
}
export default Pages;
So essentially you want router like functionality. Here is one approach:
class FirstPage extends React.Component {
render() {
//...first page content
}
}
class SecondPage extends React.Component {
render() {
//...second page content
}
}
const pages = {
first: FirstPage,
second: SecondPage
};
class App extends React.Component {
constructor() {
this.state = {
page: 'first'
};
}
render() {
const PageComponent = pages[this.state.page];
return <div>
<button onClick={() => this.setState({page: 'first'})}>First page</button>
<button onClick={() => this.setState({page: 'second'})}>Second page</button>
<PageComponent/>
</div>
}
}
There are many ways to solve this problem. But in my opinion the best solution is the one which solves the problem in a succinct manner.
Please find below the working solution which I have tried and works like a charm:
import React from "react";
class Pages extends React.Component {
state = {
activeTab: 1
};
toggle = tab => {
this.setState({
activeTab: tab
});
};
togglePage = page => {
if (page === "next") {
this.setState({
activeTab: this.state.activeTab + 1
});
} else if (page === "back") {
this.setState({
activeTab: this.state.activeTab - 1
});
}
};
render() {
return (
<div style={{ background: "#dedede" }}>
<div hidden={this.state.activeTab === 1 ? false : true}>
<p>1) What is the type of your property?</p>
<button class="btn btn-primary" onClick={() => this.toggle(2)}>
Residence
</button>
<button onClick={() => this.toggle(2)}>Commercial</button>
</div>
<div hidden={this.state.activeTab === 2 ? false : true}>
<p>2) What is the type of your commercial property?</p>
<button onClick={() => this.toggle(3)}>Office</button>
<button onClick={() => this.toggle(3)}>Restaurant</button>
<button onClick={() => this.toggle(3)}>Outlet</button>
</div>
<div hidden={this.state.activeTab === 3 ? false : true}>
<p>3) Is this your first business?</p>
<button onClick={this.NextAction}>Yes</button>
<button onClick={this.NextAction}>No</button>
</div>
<div>
<button
onClick={() => this.togglePage("back")}
name="back"
disabled={this.state.activeTab === 1 ? true : false}
>
Back
</button>
<button
onClick={() => this.togglePage("next")}
name="next"
disabled={this.state.activeTab === 3 ? true : false}
>
Next
</button>
</div>
</div>
);
}
}
export default Pages;
In react we have a hidden attribute which you can use to show/hide the elements without having to write any css for the same.
And I have tried to solve the problem with the least number of variables.
The sandbox for the same can be found here : https://codesandbox.io/s/mysolution-g8fu6
Hope this helps!

Add a class for the active button ReactJS

When you click on a button, you should change its class. When I click on one of the buttons, the class changes for both. I need the class to be added only to the active button, and if I click on another, then the first button will have the class disappear and the second will appear. Where is the error?
import React, { Component } from 'react';
class trueName extends Component {
constructor(props) {
this.state = {
name: {},
};
}
editName = (names)=>{
this.setState({name:names});
}
someFunct(name) {
this.setState({ active: name })
}
render() {
const {name}=this.state;
return(
<div >
<div className="SelectName">
<span>{this.state.name}</span>
</div>
<button
className={this.state.active === name ? 'active' : ''}
onClick={ () => {this.editName(John);this.someFunct(name)}}
key ='1'>
<span>My name John</span>
</button>
<button
className={this.state.active === name ? 'active' : ''}
onClick={ () => {this.editName(Donald);this.someFunct(name)}}
key ='2'
>
<span>My name Donald</span>
</button>
</div>
)
}
}
export default trueName;
You are setting state.name and then setting state.active to the same value, so this.state.active === this.state.name is always true and the active class gets applied.
This might help:
constructor(props) {
super(props)
this.state = {
name: null
}
}
editName = name => {
this.setState({ name: name })
}
render() {
const { name } = this.state
return (
<div>
<div className="SelectName">
<span>
<pre>{name}</pre>
</span>
</div>
<button
className={name === "John" ? "active" : ""}
onClick={() => {
this.editName("John")
}}
>
<span>My name John</span>
</button>
<button
className={name === "Donald" ? "active" : ""}
onClick={() => {
this.editName("Donald")
}}
>
<span>My name Donald</span>
</button>
</div>
)
}

onClick event on an array element javascript React

I need to do a collapsible section with a list of disciplines. The disciplines are stored in an array. I wrote an onClick event but except one discipline which I clicked, I get all of them slid down. How can I apply the event to each element, so I can decide which one will be slide down?
export default class Predictions extends React.Component {
constructor(props){
super(props);
this.handleClick = this.handleClick.bind(this);
this.state={
display: 'block',
};
}
handleClick(e) {
this.setState({
display: this.state.display === 'none' ? 'block' : 'none',
});
console.log('click', e);
};
render() {
return (
<section className="l-section c-predictions" >
<h2 className="header" >Predictions</h2>
<div className="content">
{this.props.disciplines.map((discipline, index) => {
return (
<div onClick={event => this.handleClick(discipline.id, event)} key={discipline.name} className="c-discipline">
<span className="name">{discipline.name}</span> - <span className="score">{disciplineScore(this.props.athlete.skillset, discipline.requirements)}</span>
<div style={this.state} className="element">
<p>{discipline.tags !== undefined ? discipline.tags.toString().replace(',', ', ') : ''}</p>
<p className="isIndividual">{discipline.isIndividual===true ? "Individual sport" : "Team sport"}</p>
<img src={discipline.photo}/>
</div>
</div>
)
})}
</div>
</section>
)
}
}
You need a way to identify which element has been clicked.
Here's an example:
export default class App extends React.Component {
state = {
opened: true,
selected: ''
};
toggleHidden = key => {
this.setState({ opened: !this.state.opened, selected: key });
};
render() {
return (
<div>
{arr.map((s, i) => (
<div key={i}>
<p>{s}</p>
<button onClick={() => this.toggleHidden(i)}>Toggle</button>
{!this.state.opened && this.state.selected === i && <h1>{s}</h1>}
</div>
))}
</div>
);
}
}

Resources