Simple loading before a success message - javascript

I'm trying to implement something really simple, where I want to display a loading between messages. A live example here
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
submitSuccess: false
};
}
onClick = () => {
console.log("click");
this.setState({
isLoading: false,
submitSuccess: true
});
};
render() {
return (
<div className="App">
<button onClick={this.onClick}>click</button>
{this.state.isLoading ? (
<p>loading...</p>
) : this.state.submitSuccess ? (
<p>sucess!</p>
) : (
<p>are you sure?</p>
)}
</div>
);
}
}
What I am trying to do is this example below:
Are you sure?
Loading...
Success!
But I'm not doing this right, since I'm not working properly with the ternary operators. How can I improve this?
Thanks! :)

Fixed here: https://codesandbox.io/s/rln4oz4vwq
Basic idea: Set loading to false by default, because you are not loading anything. On click of button, set loading to true as you truly are. Then on completion of async stuff, set loading to false and submitSuccess to true to indicate you are done.
Code:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
submitSuccess: false
};
}
onClick = () => {
console.log("click");
this.setState({
isLoading: true
});
//lets assume you now do some async stuff and after 2 seconds you are done
setTimeout(() => {
this.setState({
isLoading: false,
submitSuccess: true
});
}, 2000);
};
render() {
return (
<div className="App">
<button onClick={this.onClick}>click</button>
{this.state.isLoading ? (
<p>loading...</p>
) : this.state.submitSuccess ? (
<p>success!</p>
) : (
<p>are you sure?</p>
)}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Related

React onClick event in array.map()

Edit: I have included the full code for better clarity
I am not sure if this is possible. I am trying to pass an onClick event via props but the click event does not work.
The parent component looks like this:
import React from 'react'
import { getProductsById } from '../api/get'
import Product from './Product'
import { instanceOf } from 'prop-types'
import { withCookies, Cookies } from 'react-cookie'
class Cart extends React.Component {
static propTypes = {
cookies: instanceOf(Cookies).isRequired
}
constructor(props) {
super(props)
const { cookies } = props;
this.state = {
prods: cookies.get('uircartprods') || '',
collapsed: true,
total: 0,
}
this.expand = this.expand.bind(this)
this.p = [];
}
componentDidMount() {
this.getCartProducts()
}
handleClick = (o) => {
this.p.push(o.id)
const { cookies } = this.props
cookies.set('uircartprods', this.p.toString(), { path: '/' , sameSite: true})
this.setState({prods: this.p })
console.log('click')
}
getCartProducts = async () => {
let products = []
if (this.state.prods !== '') {
const ts = this.state.prods.split(',')
for (var x = 0; x < ts.length; x++) {
var p = await getProductsById(ts[x])
var importedProducts = JSON.parse(p)
importedProducts.map(product => {
const prod = <Product key={product.id} product={product} handler={() => this.handleClick(product)} />
products.push(prod)
})
}
this.setState({products: products})
}
}
expand(event) {
this.setState({collapsed: !this.state.collapsed})
}
handleCheckout() {
console.log('checkout clicked')
}
render() {
return (
<div className={this.state.collapsed ? 'collapsed' : ''}>
<h6>Your cart</h6>
<p className={this.state.prods.length ? 'hidden' : ''}>Your cart is empty</p>
{this.state.products}
<h6>Total: {this.props.total}</h6>
<button onClick={this.handleCheckout} className={this.state.prods.length ? '' : 'hidden' }>Checkout</button>
<img src="./images/paypal.png" className="paypal" alt="Paypal" />
<a className="minify" onClick={this.expand} alt="My cart"></a>
<span className={this.state.prods.length ? 'pulse' : 'hidden'}>{this.state.prods.length}</span>
</div>
)
}
}
export default withCookies(Cart)
The Product component:
import React from 'react';
class Product extends React.Component {
constructor(props) {
super(props);
this.state = {
showDetails: false,
showModal: false,
cart: []
}
this.imgPath = './images/catalog/'
}
render() {
return (
<div className="Product">
<section>
<img src={this.imgPath + this.props.product.image} />
</section>
<section>
<div>
<h2>{this.props.product.title}</h2>
<h3>{this.props.product.artist}</h3>
<p>Product: {this.props.product.product_type}</p>
<h4>${this.props.product.price}</h4>
<button className="button"
id={this.props.product.id} onClick={this.props.handler}>Add to cart</button>
</div>
</section>
</div>
)
}
}
export default Product
If I log this.props.handler I get undefined. Everything works apart from the click handler, I was wondering if it might have something to with the async function. I am very new to React, there are still some concepts I'm not sure about, so any help is appreciated.
Okay, I see a few issues here.
First, there is no need to call this.handleClick = this.handleClick.bind(this) in the constructor, because you are using an arrow function. Arrow functions do not have a this context, and instead, accessing this inside your function will use the parent this found in the Class.
Secondly, it is wrong to store components in state. Instead, map your importedProducts inside the render function.
Thirdly, the issue with your handler is that this.props.handler doesn't actually call handleClick. You will notice in the definition handler={(product) => this.handleClick} it is returning the function to the caller, but not actually calling the function.
Try this instead.
class Product extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<button className="button" id={this.props.product.id} onClick={this.props.handler}>
Add to cart
</button>
</div>
);
}
}
export default Product;
import Product from './Product'
class Cart extends React.Component {
constructor(props) {
super(props);
}
handleClick = (o) => {
console.log('click');
};
render() {
return (
<div>
{importedProducts.map((product) => {
return <Product key={product.id} product={product} handler={() => this.handleClick(product)} />;
})}
</div>
);
}
}
export default Cart;

React - Show loader on Click that already has function assigned to it

I have already a a click event within a ternary operator that does a GET request from my API. When the button is clicked, the button disappears and the data text replaces the button (button disappears). But there is a small gap of time between the get request and the text reveal. I want to put a loading message of some kind at that moment of time so the user knows something is happening. But can't seem to figure it out. Here is my code:
import React, {Component} from "react";
import axios from "axios";
export default class FoodData extends React.Component {
constructor(props) {
super(props);
this.state = {
meal: '',
clicked: false,
isLoaded: false,
}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
clicked: true,
});
}
fetchData() {
axios.get('api/menu/food')
.then(res => {
const meal= `${res.data.starters},${ res.data.price}`;
this.setState({
meal: meal,
isLoaded: true
})
console.log(meal)
})
};
combinedFunction() {
this.fetchData();
this.handleClick();
}
render(){
const {isLoaded, meal} = this.state;
return (
<div >
Dish: {
this.state.clicked ? (
this.state.menu
) : (
<button onClick={() => { this.combinedFunction() }}>Find Dish</button>
)}
</div>
);
}
}
Appreciate the help.
What you can do is add a "isLoading" state and put the values before and after your API call like so:
fetchData() {
this.setState({isLoading: true});
axios.get('api/menu/food')
.then(res => {
const meal= `${res.data.starters},${ res.data.price}`;
this.setState({
meal: meal,
isLoaded: true
isLoading: false,
})
console.log(meal)
})
};
And use that on your render to show the "loading icon"
render(){
const {isLoaded, meal, isLoading } = this.state;
return (
<div >
{isLoading ? <div>loading</div> :
Dish: {
this.state.clicked ? (
this.state.menu
) : (
<button onClick={() => { this.combinedFunction() }}>Find Dish</button>
)}
}
</div>
);
}
}
This is a working demo which shows loading when api call starts and disables button to prevent multiple api calls. I added a 2sec time out to show the demo. Check the stackblitz sample
This is the updated code, here I used a fake api (https://jsonplaceholder.typicode.com/users) to show the demo
import React, {Component} from "react";
import axios from "axios";
export default class FoodData extends React.Component {
constructor(props) {
super(props);
this.state = {
meal: '',
clicked: false,
isLoaded: false,
}
this.handleClick = this.handleClick.bind(this);
this.combinedFunction = this.combinedFunction.bind(this)
}
handleClick() {
this.setState({
clicked: true,
});
}
fetchData() {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(res => {
this.setState({
meal: res.data,
isLoaded: false
})
})
};
combinedFunction =()=> {
this.setState({isLoaded: true})
setTimeout(()=>{
this.fetchData();
},2000)
this.handleClick();
}
render(){
const {isLoaded, meal} = this.state;
return (
<>
<div >
Users:
<button onClick={this.combinedFunction } disabled={isLoaded ? true : false}>{isLoaded ? 'Loading...':'Find User'}</button>
</div>
<div>
{meal && meal.map(item =>(
<div key={item.id}>
<p>{item.id} - {item.name}</p>
</div>
))}
</div>
</>
);
}
}

React <Redirect> after transition not working

I have the following component which has a redirection route after an animation is finished, like so:
Menus.jsx
class Menus extends Component{
constructor (props) {
super(props);
this.state = {
select: 'espresso',
isLoading: false,
redirect: false
};
gotoCoffee = () => {
this.setState({isLoading:true})
setTimeout(()=>{
this.setState({isLoading:false,redirect:true})
},5000) //Replace this time with your animation time
}
renderCoffee = () => {
if (this.state.redirect) {
return (<Redirect to={`/coffee/${this.state.select}`} />)
}
}
render(){
return (
<div>
<h1 className="title is-1"><font color="#C86428">Menu</font></h1>
<hr/><br/>
<div>
{this.state.isLoading && <Brewing />}
{this.renderCoffee()}
<div onClick={this.gotoCoffee}
style={{textDecoration:'underline',cursor:'pointer'}}>
<strong><font color="#C86428">{this.state.coffees[0]}</font></strong></div>
</div>
</div>
);
}
}
export default withRouter(Menus);
The animation called onCLick:
Brewing.jsx
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import './css/mug.css'
class Brewing extends Component {
constructor (props) {
super(props);
};
render() {
return (
<div>
<div className="cup">
<div className="coffee"></div>
</div>
<div className="smoke"></div>
</div>
);
}
}
export default withRouter(Brewing);
And here redirected route component:
Coffee.jsx
class Coffees extends Component{
constructor (props) {
super(props);
this.state = {
select:'',
template:''
};
};
componentDidMount() {
if (this.props.isAuthenticated) {
this.getCoffee();
}
};
getCoffee(event) {
//const {userId} = this.props
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/coffee/espresso`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
console.log(res.data.data)
this.setState({
template: res.data.data[0].content
})
})
.catch((error) => { console.log(error); });
};
render(){
var __html = this.state.template;
var template = { __html: __html };
return (
<div id="parent">
<h1 className="title is-1"><font color="#C86428">{this.state.select} playlist</font></h1>
<hr/><br/>
<div dangerouslySetInnerHTML={template}/>
</div>
);
}
}
export default withRouter(Coffees);
but <Redirect> in Menus.jsx is not working....url changes at browser but nothing happens; only if I refresh the browser /coffee is sucessfully mounted.
What I actually need to happen:
render Menu
click on a link
click renders an animation
when animation is done, after 5 seconds,
<Redirect> to /coffee
what am I missing?
When you say url changes at browser but nothing happens; only if I refresh the browser /coffee is sucessfully mounted.
This might be the issue with your Routes.
When you redirect to path /coffee/${this.state.select}, you should have Route to handle this path.
<Route path="/coffee/:select?" render={() => ( <Coffees isAuthenticated={true}/> )}/>
Note: Be aware of adding exact prop to Route. When you add exact prop it means your path should match exactly with all the provided params.
You need to call getCoffee function in also componentDidUpdate function.
componentDidMount() {
if (this.props.isAuthenticated) {
this.getCoffee();
}
};
componentDidUpdate() {
if (this.props.isAuthenticated) {
this.getCoffee();
}
};
Your Redirect should be inside the render().
render(){
if(this.state.redirect) {
return(<Redirect to={`/coffee/${this.state.select}`} />)
} else {
return (
<div>
...your component
</div> );
}
}
Note that this way you shouldn't need your renderCoffee() function.
I'm on mobile so i wasn't able to test if it works. Let me know if this works for you.
It seems your Menu component construtor has no closing bracket.
...
class Menus extends Component{
constructor (props) {
super(props);
this.state = {
select: 'espresso',
isLoading: false,
redirect: false
};
} // did you miss this?
gotoCoffee = () => {
...

React, add onClick event on dynamically loaded div element.

Currently on my react app, I am loading many div's which is being dynamically loaded with info from a database. I am trying to make it so when I click on one of these div's a Pop-up emerges, with more in depth data related to that div. However, it does not seem to work as expected. The onClick does not work with this dynamically loaded div. I tried testing the pop-up on a standard button element on my main App component and it worked. Here is my code:
class ResultBox extends Component {
constructor(props){
super(props);
this.state = {
showPopup: false,
posts: []
};
}
togglePopup() {
this.setState({
showPopup: !this.state.showPopup
});
}
componentDidMount() {
axios.get('http://localhost:3001/api/events')
.then(res => {
let posts = res.data.map(obj => obj);
this.setState({posts});
console.log(posts);
});
}
render() { ********** Below here is where my issue is *****
return (
this.state.posts.map(events =>
<div key={events.key}
className='result_box'
onClick={this.togglePopup.bind(this)}>
<p>{events.name}</p>
{events.place && events.place.location && <p>
{events.place.location.city}</p>}
</div>
)
{this.state.showPopup ?
<Result
text='Close Me'
closePopup={this.togglePopup.bind(this)}
/>
: null
}
);
}
}
And this ResultBox is being rendered in App
class App extends Component {
render() {
return (
<div className="App">
<NavBar className="navbar-body"/>
<div className="spacer"></div>
<p className="App-intro">
Find a jam
</p>
<ResultBox />
</div>
);
}
}
The Result is simply the pop-up box component. If anyone knows how I can get this to work it would be much appreciated.
Yes, you need to have your posts data in a div, this is how I would structure it.
class ResultBox extends Component {
constructor(props){
super(props);
this.state = {
showPopup: false,
posts: []
};
this.togglePopup = this.togglePopup.bind(this);
}
togglePopup() {
this.setState({
showPopup: !this.state.showPopup
});
}
componentDidMount() {
axios.get('http://localhost:3001/api/events')
.then(res => {
let posts = res.data.map(obj => obj);
this.setState({posts});
console.log(posts);
});
}
buildRows() {
return this.state.posts.map( (events, index) =>
<div key={index} className='result_box' onClick={this.togglePopup}>
<p>{events.name}</p>
{events.place && events.place.location &&
<p>{events.place.location.city}</p>
}
</div>
);
}
render() {
let rows = this.buildRows();
return (
<div>
{rows}
{this.state.showPopup &&
<Result text='Close Me'closePopup={this.togglePopup.bind(this)}/>
}
</div>
);
}
}
export default ResultBox;

How do I send a state to a conditional component?

i have a problem..
please help me.
i'm working react js
How do I send a state to a conditional react component?
this.state = {
isTrue: false,
name: undefined
}
handleClick(e){
this.setState({
isTrue: true,
name: 'adwin'
})
}
//////// render return ///////
{this.state.isTrue ? (
<App2 name={this.state.name} />
) : ''
}
////// in App2 component //////
componentDidMount(){
console.log(this.props.name) //>>> it work undefined
}
The way you are passing prop to the child component is correct. You can try to pass props as an argument in the constructor of the child component like
class App extends React.Component {
constructor() {
super();
this.state = {
isTrue: false,
name: undefined
}
}
handleClick = (e) => {
this.setState({
isTrue: true,
name: 'adwin'
})
}
render() {
return (
<div>
{this.state.isTrue ? (
<App2 name={this.state.name} />
) : ''
}
<button onClick={this.handleClick}>Click</button>
</div>
)
}
}
class App2 extends React.Component {
constructor(props) {
super(props);
}
componentDidMount(){
console.log(this.props.name)
}
render() {
return (
<div>
{this.props.name}
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="app"></div>
if you're only looking for a conditional you can do {this.state.isTrue && <App2 name={this.state.name} />}

Categories

Resources