if else statement in render? - reactjs

I'm having some trouble with my code where I want a user to be taken to two specific pages after they click "Sign in" ONLY IF they haven't already gone through that process before. Those two pages, are below:
this.props.history.push('/decision-style');
this.props.history.push('/area-of-expertise');
How it works is, if they have already gone through the process of those two pages, I want them not to go through it again, and just to be redirected to our news page:
this.props.history.push('/news');
If they have gone through the process before, it will already have added their information in MongoDB in the documents "decisionStyle", and "role"
This is /area-of-expertise. Only want them to see this, and /decision-style if they haven't done this before, and therefore, their info isn't in Mongo
I thought I could maybe create an if... else statement in render to do something similar to what I am trying to achieve. However, that wouldn't work, so I have this code below:
class Login extends Component {
constructor(props) {
super(props);
this.state = {fact: null,
badge: null,
isLoaded: false,
error: null,
showRegistration: false,
userAuthFlow: false,
userData: {},
}
}
render() {
const self = this;
console.log(this.state.userData.decisionStyle);
// Create Registration Form
function RegistrationFormModal(props) {
return (
<Modal
{...props}
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header>
<Modal.Title id="contained-modal-title-vcenter">
Sign Up
<button className="closeBtn" onClick={self.handleCloseRegistration}></button>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Registration/>
</Modal.Body>
</Modal>
);
}
// Login page
const {error, isLoaded, fact} = this.state;
if (error) {
return (
<div>
Error: {error.messages}
</div>
);
} else if (!isLoaded) {
return (
<Spinner style={{'width': '200px', 'height': '200px', 'font-size': '50px'}} animation="border"/>
);
} else {
return (
<div id="Login">
<Fade top>
<h1 style={{'font-size': '50px'}}>CRCounter
</h1>
<p style={{'font-size': '32px'}}> {JSON.parse(JSON.stringify(fact.fact))}</p>
<p> - {JSON.parse(JSON.stringify(fact.author))} - </p>
</Fade>
<Fade bottom>
<div id="form">
<form>
</form>
<button className="confirmBtn" userAuthFlow={self.state.userData.role !== null && self.state.userData.decisionStyle !== null ? true : false}onClick = {this.handleClick}>Sign in</button>
<a id = "register" onClick={this.handleShowRegistration}>Don't have an account?</a>
<p id = "registerNote" > You won't be able to access most parts of the platform without an account! </p>
</div>
</Fade>
<RegistrationFormModal
show={this.state.showRegistration}
/>
</div>
);
}
}
The code below I created to be mainly responsible for trying to achieve what I want, but it's not working and not sure why as I am a bit of a React noob.. lol
<button className="confirmBtn" userAuthFlow={self.state.userData.role !== null && self.state.userData.decisionStyle !== null ? true : false}onClick = {this.handleClick}>Sign in</button>
The rest of the code (and updated code as well)...
/* eslint-disable require-jsdoc */
import React, {Component} from 'react';
import './Login.css';
import Fade from 'react-reveal/Fade';
import Spinner from 'react-bootstrap/Spinner';
import axios from 'axios';
import Modal from 'react-bootstrap/Modal';
import Registration from './Registration';
class Login extends Component {
constructor(props) {
super(props);
this.state = {fact: null,
badge: null,
error: null,
fact: null,
isLoaded: false,
showRegistration: false,
userAuthFlow: false,
userData: {},
}
}
handleClick = () => {
this.props.history.push('/decision-style');
}
// Registration form
handleShowRegistration = () => {
this.props.history.push('/news');
}
handleCloseRegistration = () => {
this.setState({showRegistration: false});
}
componentDidMount(sub) {
axios.get('/services/getuserdata', {
params: {ID: sub},
})
.then((response) => {
this.setState({userData: response.data});
});
// Get the facts that will be displayed under the CRCounter logo
function getFacts() {
return axios.get('/services/facts');
};
// Get the welcome badge for the user if they signded up successfully for the platform
function getBadge() {
return axios.get('/services/badge', {
params: {
name: 'welcome',
},
});
}
Promise.all([getFacts(), getBadge()])
.then((results) => {
const responseOne = results[0].data;
const responseTwo = results[1].data;
this.setState({
isLoaded: true,
fact: responseOne,
badge: responseTwo,
});
})
.catch((error) => {
this.setState({
isLoaded: true,
fact: {author: '', fact: ''}});
});
}
handleClick() {};
handleCloseRegistration() {};
handleShowRegisteration() {};
render() {
const { error, isLoaded, fact, showRegistration, userData } = this.state;
const flow = userData.role && userData.decisionStyle;
const parse = (str) => JSON.parse(JSON.stringify(str));
// Create Registration Form
const RegistrationFormModal = (props) => {
return (
<Modal
aria-labelledby="contained-modal-title-vcenter"
centered
{...props}
>
<Modal.Header>
<Modal.Title id="contained-modal-title-vcenter">
Sign Up
<button
className="closeBtn"
onClick={this.handleCloseRegistration}
>
Close Button
</button>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Registration />
</Modal.Body>
</Modal>
);
};
// Login page
if (error) {
return (
<div>
Error: {error.messages}
</div>
);
} else if (!isLoaded) {
return (
<Spinner style={{'width': '200px', 'height': '200px', 'font-size': '50px'}} animation="border"/>
);
} else {
return (
<div id="Login">
<Fade top>
<h1 style={{ 'font-size': '50px' }}>CRCounter</h1>
<p style={{ 'font-size': '32px' }}>{parse(fact.fact)}</p>
<p> - {parse(fact.author)} - </p>
</Fade>
<Fade bottom>
<div id="form">
<form>
</form>
<button
className="confirmBtn"
onClick={this.handleClick}
userAuthFlow={flow}
>
Sign in
</button>
<a
id="register"
onClick={this.handleShowRegistration}
>
Don't have an account?
</a>
<p id="registerNote" >
You won't be able to access most parts of the platform without an account!
</p>
</div>
</Fade>
<RegistrationFormModal show={showRegistration} />
</div>
);
}
}}
export default Login;

Rather than checking for falsy values, just check for truthy values instead.
Here, if we have role && decisionStyle then it is true, otherwise false.
The logical AND operator (&&) returns true if both operands are true and returns false otherwise.
self.state.userData.role && self.state.userData.decisionStyle

The logic in your condition is a little off. It should be something like userData.role && userData.decisionStyle;. I also took the liberty of destructuring some of your code, using arrow functions, removing self, and a couple of other things!
UPDATED
class Login extends Component {
constructor(props) {
super(props);
this.state = {
badge: null,
error: null,
fact: null,
isLoaded: false,
showRegistration: false,
userAuthFlow: false, // do you need this?
userData: {},
};
}
componentDidMount() {
// existing code here
}
handleClick = () => {
const { history } = this.props;
const flow = userData.role && userData.decisionStyle;
history.push(flow ? '/news' : '/decision-style');
}
handleShowRegistration = () => {
this.setState({ showRegistration: true });
}
handleCloseRegistration = () => {
this.setState({ showRegistration: false });
}
render() {
const { error, isLoaded, fact, showRegistration, userData } = this.state;
const parse = (str) => JSON.parse(JSON.stringify(str));
// Create Registration Form
const RegistrationFormModal = (props) => {
return (
<Modal
aria-labelledby="contained-modal-title-vcenter"
centered
{...props}
>
<Modal.Header>
<Modal.Title id="contained-modal-title-vcenter">
Sign Up
<button
className="closeBtn"
onClick={this.handleCloseRegistration}
>
Close Button
</button>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Registration />
</Modal.Body>
</Modal>
);
};
// Login page
if (error) return <div>Error: {error.messages}</div>;
if (!isLoaded) return (
return (
<Spinner
animation='border'
style={{
font-size: '50px',
height: '200px',
width: '200px',
}}
/>
);
);
return (
<div id="Login">
<Fade top>
<h1 style={{ 'font-size': '50px' }}>CRCounter</h1>
<p style={{ 'font-size': '32px' }}>{parse(fact.fact)}</p>
<p> - {parse(fact.author)} - </p>
</Fade>
<Fade bottom>
<div id="form">
<form>
</form>
<button
className="confirmBtn"
onClick={this.handleClick}
>
Sign in
</button>
<a
id="register"
onClick={this.handleShowRegistration}
>
Don't have an account?
</a>
<p id="registerNote" >
You won't be able to access most parts of the platform without an account!
</p>
</div>
</Fade>
<RegistrationFormModal show={showRegistration} />
</div>
);
}
}

Related

Use react modal bootstrap to edit items in a map list

My application have two components, one renders a list and the other render the form:
I would like to use the same component to create new categories and also to edit the categories that already exists.
The create categories is working fine already, but to edit, needs to pass the id of the category in the list to the form inside the modal, as i am new into react, i would like some help. Thanks in advance.
The list file is called,
Categories.jsx
import React, { Component } from 'react'
import { Alert, Modal, Button } from "react-bootstrap";
import Datatable from '../../../globalcomponents/datatable/Datatable';
import CategoryForm from './CategoryForm';
const Api = require('../../api/CategoriesApi.js')
class Categories extends Component {
constructor(props) {
super(props)
this.state = {
categories: [],
isLoaded: false,
error: null,
isOpen: false
}
}
openModal = () => this.setState({ isOpen: true });
closeModal = () => this.setState({ isOpen: false });
componentDidMount() {
Api.getCategories()
.then(response => {
const [error, data] = response
if (error) {
this.setState({
isLoaded: true,
categories: [],
error: data
})
} else {
this.setState({
isLoaded: true,
categories: data
})
}
})
}
render() {
const { error, isLoaded, categories } = this.state
if (error) {
return (
<Alert color="danger">
Error: {error}
</Alert>
)
} else if (!isLoaded) {
return (
<Alert color="primary">
Loading...
</Alert>
)
} else {
return (
<>
<Button className="float-right" variant="primary" onClick={this.openModal}>
Adicionar
</Button>
<h4 className="mt-4 mb-4">Categorias de investimentos</h4>
<Datatable>
<table className="table table-striped my-4 w-100">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Url (Slug)</th>
<th></th>
</tr>
</thead>
<tbody>
{categories.map(category => (
<tr key={category.id}>
<td>{category.id}</td>
<td>{category.title}</td>
<td>{category.slug}</td>
<td>
<Button className="float-right mr-2" variant="primary" onClick={this.openModal}>
Modal Edit
</Button>
</td>
</tr>
))}
</tbody>
</table>
</Datatable>
<Modal show={this.state.isOpen} onHide={this.closeModal}>
<Modal.Header closeButton>
<Modal.Title>Adicionar / Editar</Modal.Title>
</Modal.Header>
<Modal.Body>
<CategoryForm />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.closeModal}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
)
}
}
}
export default Categories
The form file is used to create or edit categories. And it is called:
CategoryForm.jsx
import React, { Component } from 'react'
import { Redirect } from 'react-router'
import { Row, Col, Alert, Button, Form, FormGroup, Label, Input } from 'reactstrap'
const Api = require('../../api/CategoriesApi.js')
class CategoryForm extends Component {
constructor(props) {
super(props)
this.state = {
category: {
id: this.getCategoryId(props),
title: '',
slug: '',
},
redirect: null,
errors: []
}
this.setTitle = this.setTitle.bind(this)
this.setSlug = this.setSlug.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
getCategoryId(props) {
try {
return props.match.params.id
} catch (error) {
return null
}
}
setTitle(event) {
let newVal = event.target.value || ''
this.setFieldState('title', newVal)
}
setSlug(event) {
let newVal = event.target.value || ''
this.setFieldState('slug', newVal)
}
setFieldState(field, newVal) {
this.setState((prevState) => {
let newState = prevState
newState.category[field] = newVal
return newState
})
}
handleSubmit(event) {
event.preventDefault()
let category = {
title: this.state.category.title,
slug: this.state.category.slug,
}
Api.saveCategory(category, this.state.category.id)
.then(response => {
const [error, errors] = response
if (error) {
this.setState({
errors: errors
})
} else {
this.setState({
// reload categories
redirect: '/admin'
})
}
})
}
componentDidMount() {
if (this.state.category.id) {
Api.getCategory(this.state.category.id)
.then(response => {
const [error, data] = response
if (error) {
this.setState({
errors: data
})
} else {
this.setState({
category: data,
errors: []
})
}
})
}
}
render() {
const { redirect, category, errors } = this.state
if (redirect) {
return (
<Redirect to={redirect} />
)
} else {
return (
<>
<Row>
<Col>
{errors.length > 0 &&
<div>
{errors.map((error, index) =>
<Alert color="danger" key={index}>
{error}
</Alert>
)}
</div>
}
<Form onSubmit={this.handleSubmit}>
<FormGroup>
<Label for="title">Title</Label>
<Input type="text" name="title" id="title" value={category.title} placeholder="Enter title" onChange={this.setTitle} />
</FormGroup>
<FormGroup>
<Label for="slug">Slug</Label>
<Input type="text" name="slug" id="slug" value={category.slug} placeholder="Enter slug" onChange={this.setSlug} />
</FormGroup>
<Button color="success">Submit</Button>
</Form>
</Col>
</Row>
</>
)
}
}
}
export default CategoryForm
You are not passing the id in Categories.jsx. either you can set the id in the history state or do pass it by component
prop drill.
setting the state in history Programmatically set params in React Router v4
Or You can do Pass the id to the Component and handle in Component
DidMount Event.
here is the code sandbox link
Categories.jsx
/** Create a id variable in state. **/
class Categories extends Component {
constructor(props) {
super(props);
this.state = {
categories: [],
isLoaded: false,
error: null,
isOpen: false,
--> id: null <--
};
}
/** change the openModal code to something like this. **/
openModal = (id) => {
this.setState( (prev) => {
const state = prev.state;
return { ...state, id: id, isOpen:true };
});
};
/** while Onclick set the id in the state. **/
<Datatable>
<table className="table table-striped my-4 w-100">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Url (Slug)</th>
<th></th>
</tr>
</thead>
<tbody>
{categories.map((category) => (
<tr key={category.id}>
<td>{category.id}</td>
<td>{category.title}</td>
<td>{category.slug}</td>
<td>
<Button
className="float-right mr-2"
variant="primary"
--> onClick={() =>this.openModal(category.id)}
>
Modal Edit
</Button>
</td>
</tr>
))}
</tbody>
</table>
</Datatable>
/** Pass the id prop for CategoryForm Component in Modal body from the state. **/
<Modal show={this.state.isOpen} onHide={this.closeModal} >
<Modal.Header closeButton>
<Modal.Title>Adicionar / Editar</Modal.Title>
</Modal.Header>
<Modal.Body>
--> <CategoryForm id={this.state.id || null} />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick= {this.closeModal}>
Close
</Button>
</Modal.Footer>
</Moddal>
CategoryForm.jsx
In the componentDidMount conditionally check if there is id variable in props.
componentDidMount() {
**// Check if props.id is available**
if (** this.state.category.id || this.props.id **) {
**const id = this.state.category.id || this.props.id;**
Api.getCategory(id).then((response) => {
const [error, data] = response;
if (error) {
this.setState({
errors: data
});
} else {
alert(id);
this.setState({
category: data,
errors: []
});
}
});
}
}
You can add more state to Categories to keep track of additional data about the modal.
*I have only included the highlights in the code here; lots was left out for brevity.
In Categories.jsx:
constructor(props) {
super(props)
this.state = {
categories: [],
isLoaded: false,
error: null,
isOpen: false,
modalData: null,
}
}
openModal = (modalData) => this.setState({ isOpen: true, modalData });
closeModal = () => this.setState({ isOpen: false, modalData: null });
//'create' dialog button
<Button className="float-right" variant="primary" onClick={e => this.openModal({modalType: 'create'})}>
Adicionar
</Button>
//here are the table rows:
{categories.map(category => (
<tr key={category.id}>
<td>{category.id}</td>
<td>{category.title}</td>
<td>{category.slug}</td>
<td>
<Button className="float-right mr-2" variant="primary" onClick={e => this.openModal({modalType: 'edit', categoryId: category.id})}>
Modal Edit
</Button>
</td>
</tr>
))}
//the modal. pass modalData as a prop:
<Modal show={this.state.isOpen} onHide={this.closeModal}>
<Modal.Header closeButton>
<Modal.Title>Adicionar / Editar</Modal.Title>
</Modal.Header>
<Modal.Body>
<CategoryForm modalData={modalData} />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.closeModal}>
Close
</Button>
</Modal.Footer>
</Modal>
In CategoryForm.jsx:
//get id from props:
getCategoryId(props) {
return (props.modalData.modalType === 'edit') ? props.modalData.categoryId : false;
//I don't know if this needs to be here:
//try {
// return props.match.params.id
//} catch (error) {
// return null
//}
}
You might have to refactor CategoryForm.jsx. For instance, the categoryId is now a prop, so it doesn't need to be duplicated in state.

React Modal with axios infinite loop

The print statement in my if (this.props.currentNode.getAttribute("label").toLowerCase() === "data")
is being called infinite times which keeps on printing in modal and sending a post request until the modal is closed.
This does not happen when I put the post call in ComponentDidMount
Can anyone please explain why this is happening and what I can do to avoid this?
class ConfigurationModal extends React.Component {
constructor(props) {
super(props);
this.state = {
selectSource: [],
};
}
el = document.createElement("div");
componentDidMount() {
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
jsonIsEmpty = (obj) => {
return obj === "[object Object]" ? true : false;
};
render() {
// The gray background
const backdropStyle = {
position: "fixed",
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: "rgba(0,0,0,0.3)",
padding: 50,
};
if (this.props.currentNode.getAttribute("label").toLowerCase() === "data") {
console.log("in modal")
http.post("configure_get_sources", {
headers: {
"content-type": "application/json",
},
})
.then((res) => {
this.setState({ selectSource: res.data });
});
var tempConfig = this.jsonIsEmpty(
this.props.currentNode.getAttribute("configuration")
)
? {}
: JSON.parse(this.props.currentNode.getAttribute("configuration"));
let renderedSources = this.state.selectSource.map((item, i) => {
return (
<option value={item} key={i} selected={item === tempConfig["Source"]}>
{item}
</option>
);
});
return ReactDOM.createPortal(
<div className="backdrop" style={backdropStyle}>
<Modal.Dialog>
<Modal.Header>
<Modal.Title>Configure Node</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="container">
<div className="row">
<label className="col-md-4">Data Source: </label>
<select className="col-md-7" id="dataSelect">
{renderedSources}
</select>
</div>
<div className="row col-md-3"></div>
</div>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.props.onClose}>
Close
</Button>
<Button variant="primary" onClick={this.props.saveModal}>
Save changes
</Button>
</Modal.Footer>
</Modal.Dialog>
</div>,
this.el
);
}
return "";
}
}
export default ConfigurationModal;
You're modifying component's state inside render, this causing an infinity loop. Because of react re-rendering component after each props or state change. https://reactjs.org/docs/faq-state.html

React-Bootstrap modal only take last item of map

I'm a beginner in coding. I'm creating my graphic designer portfolio. I've put all the portfolio content (thumbnail, client name, description...) into an array called "portfolio" in a JSON file. Each item the array is a different project.
I display a galery of thumbnails, and when I click on a thumbnail, a modal opens with the project details.
I map on my "portfolio" array to display the galery, that works. But when I open the modal, it always display the last item of my array.
import React from 'react';
import Modal from 'react-bootstrap/Modal';
class Portfolio extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
error: null,
isLoaded: false,
items: [],
projectName: "",
show: false
};
this.handleShow = () => {
this.setState({ show: true });
};
this.handleClose = () => {
this.setState({ show: false });
};
}
componentDidMount() {
fetch("https://api.myjson.com/bins/1cbaj5")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.portfolio
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const {error, isLoaded, items} = this.state;
if (error) {
return <div>Erreur : {error.message}</div>;
} else if (!isLoaded) {
return <div>Chargement…</div>;
} else {
return (
<div id="portfolio">
<h2>Portfolio</h2>
<ul className="portfolio-list">
{items.map(item => (
<li key={item.client}>
<div className="vignette">
<button onClick={() => this.handleShow()}>
<h4>{item.client}</h4>
<div className="filtre"></div>
<img src={item.vignette} alt={item.titre} />
</button>
<Modal show={this.state.show} onHide={this.handleClose}>
<Modal.Header closeButton>
<h3>{item.client}</h3>
</Modal.Header>
<Modal.Body>
<p>{item.description}</p>
</Modal.Body>
</Modal>
</div>
</li>
))}
</ul>
</div>
);
}
}
}
export default Portfolio;
I would like the modal to display the corresponding project details.
Thank you for your help.
You need to have only 1 modal and pass data dynamically on item click,
<ul className="portfolio-list">
{items.map(item => (
<li key={item.client}>
<div className="vignette">
<button onClick={()=> this.handleShow(item)}> //Pass complete item object here
<h4>{item.client}</h4>
<div className="filtre"></div>
<img src={item.vignette} alt={item.titre} />
</button>
</div>
</li>
))}
<Modal show={this.state.show} onHide={this.handleClose}> //Only one modal
<Modal.Header closeButton>
<h3>{this.state.activeItem.client}</h3>
</Modal.Header>
<Modal.Body>
<p>{this.state.activeItem.description}</p>
</Modal.Body>
</Modal>
</ul>
Now in your handleShow function you can set item to state,
this.handleShow = (item) => {
this.setState({activeItem:item}, ()=> this.setState({ show: true }));
};
Use callback to show modal, which makes sure activeItem have latest clicked item.
Initial state,
this.state = {
error: null,
isLoaded: false,
items: [],
projectName: "",
show: false,
activeItem:'' //new added
};
The previous answer is great. In functional components it is possible to create a function for handling both states and it works perfectly.
export const TeamCollection = () => {
const [modalShow, setModalShow] = useState(false);
const [name, setName] = useState("");
const handleName = (name) => {
setName(name);
setModalShow(true);
};
return (
<Container>
<Row>
{team_data.map((item) => (
<Col key={item.id}>
<div style={{ width: "175px" }}>
<ImageContainer>
<LinkContainer onClick={() => handleName(item.name)}>
<BackgroundImage
style={{ backgroundImage: `url(${item.imageUrl})` }}
/>
</LinkContainer>
</ImageContainer>
<h4>{item.name}</h4>
<p>{item.position}</p>
</div>
</Col>
))}
</Row>
<RouteModal
data={name}
show={modalShow}
onHide={() => setModalShow(false)}
/>
</Container>
);
};
export default TeamCollection;

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!

Reactjs component modal onclick div

I am trying to make a modal component which I can reuse, but I don't get what I am doing wrong here. The Modal is not appearing. Can anyone help me out?
Little explanation about my app.
This app is loading a JSON url and shows a list of products, which can be marked as done. If you click the div plaatjediv you should get a popup (the modal) with details info over the clicked product.
EDIT: Edited the code as suggested here. I can see the state change to true and false if I click the div, but the Modal is still not appearing.
my code
App.js
import React from 'react';
import ProductModal from './ProductModal.js';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleModal = this.toggleModal.bind(this);
this.state = {
isLoading: true,
orders: [],
dealtOrders: [],
open: false
}
}
toggleModal() {
this.setState({
open: !this.state.open
});
}
componentWillMount() {
localStorage.getItem('orders') && this.setState({
orders: JSON.parse(localStorage.getItem('orders')),
isLoading: false
})
}
componentDidMount() {
if (!localStorage.getItem('orders')){
this.fetchData();
} else {
console.log('Using data from localstorage');
}
}
fetchData() {
fetch('http://localhost:54408/api/orders/all/26-03-2018')
.then(response => response.json())
.then(parsedJSON => parsedJSON.map(product => (
{
productname: `${product.ProductName}`,
image: `${product.Image}`,
quantity: `${product.Quantity}`,
isconfirmed: `${product.IsConfirmed}`,
orderid: `${product.OrderId}`
}
)))
.then(orders => this.setState({
orders,
isLoading: false
}))
.catch(error => console.log('parsing failed', error))
}
render() {
this.handleDoneAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let prevOrders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const itemToMoveAtLast = prevOrders.splice(itemIndex, 1);
const addToDealtOrders = dealtOrders.concat(itemToMoveAtLast);
this.setState({dealtOrders: addToDealtOrders});
this.setState({orders: prevOrders});
};
this.handleUndoAction = event =>
{
let itemIndex = event.target.getAttribute("data-itemIndex");
let orders = [...this.state.orders];
let dealtOrders = [...this.state.dealtOrders];
const undoDealtOrder = dealtOrders.splice(itemIndex, 1);
const addToOrders = orders.concat(undoDealtOrder);
this.setState({orders: addToOrders});
this.setState({dealtOrders: dealtOrders});
};
const {isLoading, orders, dealtOrders,open} = this.state;
return (
<div>
<header>
<img src="/images/header.jpg"/>
<h1>Boodschappenlijstje <button className="btn btn-sm btn-danger">Reload</button></h1>
</header>
<ProductModal open={open} />
<div className={`content ${isLoading ? 'is-loading' : ''}`}>
<div className="panel">
{
!isLoading && orders.length > 0 ? orders.map((order, index) => {
const {productname, image, quantity, orderid} = order;
return<div className="product" key={orderid}>
<div className="plaatjediv" onClick={this.toggleModal}>
<img className="img-responsive" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleDoneAction}>Done</button>
</div>
</div>
}) : null
}
</div>
<h2>Mandje</h2>
<div className="panel">
{
!isLoading && dealtOrders.length > 0 ? dealtOrders.map((dorder, index) => {
const {productname, image, quantity, orderid} = dorder;
return<div className="productDone" key={index}>
<div className="plaatjediv">
<img className="img-responsive" src={image} />
</div>
<div className="productInfo">
<p>{productname}</p>
<p>Aantal: {quantity}</p>
</div>
<div className="bdone">
<button className="btn btn-lg btn-default btndone" data-itemIndex={index} onClick={this.handleUndoAction}>Undo</button>
</div>
</div>
}) : null
}
</div>
<div className="loader">
<div className="icon"></div>
</div>
</div>
</div>
);
}
} export default App;
ProductModal.js
import React from 'react';
class ProductModal extends React.Component {
constructor() {
super();
}
render() {
const open = this.props.open;
return (
<div className={'modal fade'+(open ? '' : 'hide')} tabindex="-1" role="dialog">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">test</h4>
</div>
<div className="modal-body">
test
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" className="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
)
}
}
export default ProductModal;
I am unsure what your issue is from your question but I am guessing your model doesn't open?
When you set state, you need to set it to the opposite of this.state.open
You can do it like this:
toggleModal() {
this.setState({
open: !this.state.open
});
}
I can't see where the modal is supposed to be rendered. You have to add it to render function of your "App" class. like this:
render() {
...
return(
<ProductModal open={true} />
...
):
}
and also, in your toggleModal function, do something like this:
this.setState({ open: !this.state.open});
Hope this solves the issue.
The issue is that you do not have your <ProductModal /> as a component in your <App /> In addition to setting your open state, once shown, it will (or should) never hide because you will not be able to toggle it again using your button, and you also do not have any keybindings within your <ProductModal /> itself.
I would suggest you bind an event listener within <ProductModal /> to
Check is ESC key is pressed
Bind a Cancel/Close button (in addition to a header x button).
Listen for if anywhere outside of your dialog is clicked, dismiss the modal.
You will also need to pass a handler from <App /> down to <ProductModal /> to notify when the modal has been closed.
In your App.js
handleClose() {
this.setState({
open: false
});
}
render() {
return (
...
<ProductModal open={this.state.open} handleClose={this.handleClose.bind(this)} />
)
}
Then in your ProductModal.js
handleClose() {
this.props.handleClose();
}
Observe the following using my sandbox:
https://stackblitz.com/edit/react-98m4cr
You'll see that I've implemented the handleClose event to control the state back up to the parent. In addition, you may want to add listeners as mentioned above, all triggering handleClose in the end; just remember to unbind them in ProductModal.js componentWillUnmount.

Resources