Hide all div and show one div on clicking multiple button - reactjs

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!

Related

Add class to following buttons inside a map function

I have a function that adds class onClick.
import * as React from 'react'
class ThisClass extends React.Component<any,any> {
constructor(){
super()
this.state = {active: ''}
this.ThisFunction = this.ThisFunction.bind(this)
}
const items = ['button1', 'button2', 'button3']
ThisFunction (i) {
this.setState({
active: i
})
}
render(){
return(
<div>
{
items.map((item, i) => {
return(
<button
onClick={()=>this.ThisFunction(i)}>{item}
className={`this_button ${this.state.active ? 'active' : ''}`}
{item}
</button>
)
})
}
</div>
)
}
}
export default ThisClass
What's suppose to happen:
<div>
<button class='this_button active'>button1</button>
<button class='this_button active'>button2</button>
<button class='this_button'>button3</button>
</div>
What's really happening:
<div>
<button class='this_button active'>button1</button>
<button class='this_button'>button2</button>
<button class='this_button'>button3</button>
</div>
I need to have a maximum of two active' class onClick function. Is there any way to get around this?
Change your state to hold an array of active indices and change the handler to only maintain the last two set (i.e. kick out the least recently used)
state = {
active: []
};
thisFunction = i => {
if (this.state.active.includes(i)) return; // already active!!
const active = [...this.state.active, i].slice(-2); // keep last 2
this.setState({ active });
};
You also had some syntax errors in the button rendering. The classname was being set as part of the button text. Set the active class if this.state.active array includes the active index
{["button1", "button2", "button3"].map((item, i) => {
return (
<button
onClick={() => this.thisFunction(i)}
className={`this_button ${
this.state.active.includes(i) ? "active" : ""
}`}
>
{item}
</button>
);
})}
There are some major errors in your code.
You are putting the className property as a child on the button, is an attribute instead
Every child on a list e.g your buttons needs to have a key
You are binding twice the function, one on the constructor and another one on the onClick event using an arrow function. Bind a function
class ThisClass extends React.Component {
constructor(){
super()
this.state = {active: ''}
}
ThisFunction (i) {
this.setState({
active: i
})
}
render(){
const items = ['button1', 'button2', 'button3']
return(
<div>
{
items.map((item, i) => {
return(
<button
key={i}
onClick={() => this.ThisFunction(i)}
className={`this_button ${this.state.active === i ? 'active' : ''}`}>
{item}
</button>
)
})
}
</div>
)
}
}
ReactDOM.render(
<ThisClass />,
document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

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

To Do List: Delete Button Removes Info from Wrong List Item

I am creating a basic React application which is essentially a to do list. The user can enter text and add an item to the list, which is an array that is rendered with the map method. Each item can be upvoted, downvoted, or deleted. The input text, number of upvotes, and number of downvotes are displayed on each item.
Here's the problem: When I click the delete button on an item, the input text from that item is deleted, but not the upvotes or downvotes. The upvotes and downvotes are always removed from the last item in the array. So when the list is re-rendered, the votes become all mixed up.
I've included the code from both my parent component (Category) and child component (Item). Sorry if it's a lot of code but I've had trouble pinpointing the source of the problem.
class Category extends Component {
state = {
inputValue: '',
items: this.props.items,
};
handleSubmit = e => {
e.preventDefault();
this.setState({ items: this.state.items.concat(this.state.inputValue) })
this.setState({ inputValue: "" })
}
updateInputValue(evt) {
this.setState({
inputValue: evt.target.value
});
}
handleDelete = index => {
const newItems = this.state.items.filter((item, i) => {
return index !== i;
});
return this.setState({ items: newItems });
}
render() {
return (
<div className="category">
<h2>{this.props.name}</h2>
<form onSubmit={this.handleSubmit}>
<input value={this.state.inputValue} onChange={e => this.updateInputValue(e)}/>
<button type="submit">Submit</button>
</form>
{/* Enter all items: */}
{this.state.items.length > 0 ?
this.state.items.map((item, index) => {
const key = index;
return <Item
key={key}
text={item}
handleDelete={this.handleDelete.bind(this, index)}
/>
}) : <p>There are no items here.</p>
}
</div>
)
}
}
class Item extends Component {
state = {
likes: 0,
dislikes: 0
}
handleUpvote = () => {
return this.setState({ likes: this.state.likes + 1 });
}
handleDownvote = () => {
return this.setState({ dislikes: this.state.dislikes + 1 });
}
render() {
return (
<div className="item">
<div className="move-item">
<FontAwesomeIcon icon="chevron-left" />
</div>
<div className="item-content">
<div className="item-text">
<p>{this.props.text}</p>
</div>
<div className="item-actions">
<div className="item-action-icon">
<button onClick={this.handleUpvote}>
<FontAwesomeIcon icon="thumbs-up" />
</button>
<span> {this.state.likes}</span>
</div>
<div className="item-action-icon">
<button onClick={this.handleDownvote}>
<FontAwesomeIcon icon="thumbs-down" />
</button>
<span> {this.state.dislikes}</span>
</div>
<div className="item-action-icon">
<button onClick={this.props.handleDelete}>
<FontAwesomeIcon icon="trash-alt" />
</button>
</div>
</div>
</div>
<div className="move-item">
<FontAwesomeIcon icon="chevron-right" />
</div>
</div>
)
}
}
You need to lift up your state of likes/dislikes to be associated with each item. Otherwise, each one will be associated by position (AKA index), and once you remove one item, the index that dissapears is the last one, causing you to mess up the votes
your parent state would be like
items: this.props.items.map(item => ({ item, likes: 0, dislikes. 0 });
handleUpvote and handleDownvote should be on your Category component, and passed by props to your Item, which will call them passing the item.
That way you can update your likes/dislikes

React Toggle Active Class between multiple Reactstrap Buttons

I am new to React and Reactstrap. I am trying to set the active class or active attribute to a single button onclick. My current setup toggles all the buttons at the same time. What the best way to toggle the active states independently between the buttons states when clicked?
import React, { Component } from 'react'
import { Button } from "reactstrap";
export class Payment extends Component {
constructor(props) {
super(props);
this.state = {
paymentSelection: 'credit',
active: false
}
}
toggleContent = (event) => {
const currentState = this.state.active;
event.preventDefault();
this.setState({
paymentSelection: event.target.value,
active: !currentState
})
}
switchContent = (value) => {
switch (value) {
case 'credit':
return <div>
<p>Credit Content Here</p>
</div>;
case 'paypal':
return <div>
<p>PayPal Content Here</p>
</div>;
case 'amazon':
return <div>
<p>Amazon Content Here</p>
</div>;
case 'more':
return <div>
<p>More Content Here</p>
</div>;
default:
return null;
}
}
render() {
const { paymentSelection } = this.state;
const { active } = this.state;
return (
<div className="container-fluid payment-btn-group">
<Button outline className={active ? 'active': null} color="secondary" value="credit" onClick={this.toggleContent} >Credit Card</Button>
<Button outline className={active ? 'active': null} color="secondary" value="paypal" onClick={this.toggleContent} >PayPal</Button>
<Button outline className={active ? 'active': null} color="secondary" value="amazon" onClick={this.toggleContent} >Amazon</Button>
<Button outline className={active ? 'active': null} color="secondary" value="more" onClick={this.toggleContent} >+ More</Button>
{this.switchContent(paymentSelection)}
</div>
);
}
}
export default Payment
You can set active class like this
className={paymentSelection ==='credit' ? 'active' : null}
You already have payment selection in the state you just need to check that apply it.
Demo

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