How to get the promise value in reactjs? - reactjs

I am trying to render multiple images, stored on firebase, on a single page.
This is the function to get the image url:
async getTokenImage(fileName) {
const ref = firebase.storage().ref('token/' + fileName);
const url = await ref.getDownloadURL();
console.log('in here')
return url
}
This is the function call:
<div
class="card-img-top"
style={{
backgroundColor: '#'+token.color,
backgroundImage: 'url('+this.getTokenImage(token.fileName).then((value) => {return value})+')'
}}></div>
console logging the "value" gives the needed image url
but returning the "value" gives nothing.
for the sake of testing, I have done this:
let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)
which results in:
So how do I get the image url within the initial function call?
Part of grandfather component:
<MintedTokens
account={this.state.account}
contract={this.state.contract}
tokens={this.state.tokens} />
Father component:
import React from 'react';
import "./minted-tokens.scoped.css";
import MultipleMintedTokens from './multiple-minted-tokens.js';
import SingleMintedTokens from './single-minted-token.js';
class MintedTokens extends React.Component {
render() {
const color = window.location.pathname.split('/')[2]
let display
if(color == undefined) {
display = <MultipleMintedTokens
account={this.props.account}
tokens={this.props.tokens}></MultipleMintedTokens>
} else {
display = <SingleMintedTokens
account={this.props.account}
color={color}
tokens={this.props.tokens}></SingleMintedTokens>
}
return (
<div class="Minted-Token">
{display}
</div>
);
}
}
export default MintedTokens;
Child component (where the multiple images need to be rendered):
import React from 'react';
import ether from '../../assets/ether.svg'
import firebase from "../../firebase.js"
import { Link } from 'react-router-dom';
class MultipleMintedTokens extends React.Component {
async getTokenImage(fileName) {
const ref = firebase.storage().ref('token/' + fileName);
const url = await ref.getDownloadURL();
console.log('in here')
return url
}
render() {
return (
<div class="welcome minted-tokens">
<h1 class="">Minted Tokens</h1>
<div class="row">
{this.props.tokens.map((token, key) => {
let name = '',
description = ''
if(token.to == this.props.account) {
token.name.length >= 17 ? name = token.name.slice(0,17) + '...' : name = token.name
token.description.length >= 28 ? description = token.description.slice(0,29) + '...' : description = token.description
let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)
return (
<Link to={token.tokenURI} key={key}>
<div class='token-id'>{token.id}</div>
<div class="card card-width">
<div
class="card-img-top"
style={{
backgroundColor: '#'+token.color,
backgroundImage: 'url('+this.getTokenImage(token.fileName).then((value) => {return value})+')'
}}></div>
<h5 class="card-header" style={{
backgroundColor: '#'+token.color,
color: '#'+token.fontColor}}>{name}</h5>
<div class="card-body">
<p class="card-text">{description}</p>
<div class="foot-of-card">
<span class="row price-row">
<img src={ether} alt="ether" class="icon" />
<p class="card-text">{token.price}</p>
</span>
<p class="card-text datetime">{token.dateTime}</p>
</div>
</div>
</div>
</Link>
)
}
})}
</div>
</div>
);
}
}
export default MultipleMintedTokens;

I could not test it well but you can try the following:
Add:
constructor(props) {
super(props);
this.state = {
urlImgs: [],
};
}
...
componentDidMount() {
const { tokens } = this.props;
const promiseArray = tokens.map((token) => getTokenImage(token.fileName));
Promise.all(promiseArray)
.then(valueArray => {
this.setState(prevState => ({
...prevState,
urlImgs: valueArray
}))
})
.catch(err => console.log(err));
}
...
backgroundImage: `url(${urlImgs[key] ?? null})`
All Code:
class MultipleMintedTokens extends React.Component {
constructor(props) {
super(props);
this.state = {
urlImgs: [],
};
}
async getTokenImage(fileName) {
const ref = firebase.storage().ref('token/' + fileName);
const url = await ref.getDownloadURL();
console.log('in here')
return url
}
componentDidMount() {
const { tokens } = this.props;
const promiseArray = tokens.map((token) => getTokenImage(token.fileName));
Promise.all(promiseArray)
.then(valueArray => {
this.setState(prevState => ({
...prevState,
urlImgs: valueArray
}))
})
.catch(err => console.log(err));
}
render() {
const { urlImgs } = this.state;
return (
<div class="welcome minted-tokens">
<h1 class="">Minted Tokens</h1>
<div class="row">
{this.props.tokens.map((token, key) => {
let name = '',
description = ''
if(token.to == this.props.account) {
token.name.length >= 17 ? name = token.name.slice(0,17) + '...' : name = token.name
token.description.length >= 28 ? description = token.description.slice(0,29) + '...' : description = token.description
let image = this.getTokenImage(token.fileName).then((result) => {return result})
console.log(image)
return (
<Link to={token.tokenURI} key={key}>
<div class='token-id'>{token.id}</div>
<div class="card card-width">
<div
class="card-img-top"
style={{
backgroundColor: '#'+token.color,
backgroundImage: `url(${urlImgs[key] ?? null})`
}}></div>
<h5 class="card-header" style={{
backgroundColor: '#'+token.color,
color: '#'+token.fontColor}}>{name}</h5>
<div class="card-body">
<p class="card-text">{description}</p>
<div class="foot-of-card">
<span class="row price-row">
<img src={ether} alt="ether" class="icon" />
<p class="card-text">{token.price}</p>
</span>
<p class="card-text datetime">{token.dateTime}</p>
</div>
</div>
</div>
</Link>
)
}
})}
</div>
</div>
);
}
}
export default MultipleMintedTokens;
if you have React with version > 16.8, I advice you to start using Stateless Components and Hooks. And read about the lifecycle of React and the render method here:

Related

React Component Not Being Populated even after updating state and executing forEach

I want to fetch memes from the imgflip meme API, and even though I get to see the HTTP response in my console, the DOM is not getting populated, I tried everything.
export default class MemesContainer extends Component {
constructor() {
super();
this.state = {
memes: [],
};
}
async componentDidMount() {
let req = await fetch("https://api.imgflip.com/get_memes");
let res = await req.json();
console.log(res.data.memes);
await this.setState({ memes: res.data.memes });
}
render() {
return (
<div className="container d-flex justify-content-center flex-wrap">
{this.state.memes.map((e) => {
<div className="card m-3" style={{ width: "18" + "rem" }}>
<img src={e.url} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title">{e.name}</h5>
</div>
</div>;
})}
</div>
);
}
}
ForEach Doesn't return anything, it just executes the code. At this point you should use Array.map as it is meant to loop through array and transform it's values and return them.
export default class MemesContainer extends Component {
constructor() {
super();
this.state = {
memes: [],
};
}
async componentDidMount() {
let req = await fetch("https://api.imgflip.com/get_memes");
let res = await req.json();
console.log(res.data.memes);
await this.setState({ memes: res.data.memes });
}
render() {
return (
<div className="container d-flex justify-content-center flex-wrap">
{this.state.memes.map((e) => {
return (
<div className="card m-3" style={{ width: "18" + "rem" }}>
<img src={e.url} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title">{e.name}</h5>
</div>
</div>
)
})}
</div>
);
}
}

React parent component needs child function to return data

I think I need a call back function, but do not understand the proper syntax given a parent component calling a child function.
Here is the stripped down parent component followed by the function FilesUpload.
I need the File.Name from child returned and setState({fileName}) in parent component.
Hopefully painfully obvious to someone who knows how to do this.
Thank you in advance for solution.
Rob
#davidsz - any ideas?
...
//Stripped down ParentComponent.jsx
import React, { Component } from 'react'
import FilesUpload from "../Services/FilesUpload";
class ParentComponent extends Component {
constructor(props) {
super(props)
this.state = {
fileName: null
}
this.changefileNameHandler = this.changefileNameHandler.bind(this);
}
changefileNameHandler= (event) => {
this.setState({fileName: event.target.value});
}
componentDidMount(){
}
render() {
return (
<div>
<td>this.state.fileName </td>
<FilesUpload onUpdate={this.changefileNameHandler}/>
</div>
)
}
}
export default ParentComponent
//functional service FilesUpload.js
import React, { useState, useEffect, useRef } from "react";
import UploadService from "../Services/FileUploadService";
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
const UploadFiles = () => {
const [selectedFiles, setSelectedFiles] = useState(undefined);
const [progressInfos, setProgressInfos] = useState({ val: [] });
const [message, setMessage] = useState([]);
const [fileInfos, setFileInfos] = useState([]);
const progressInfosRef = useRef(null)
useEffect(() => {
UploadService.getFiles().then((response) => {
setFileInfos(response.data);
});
}, []);
const selectFiles = (event) => {
setSelectedFiles(event.target.files);
setProgressInfos({ val: [] });
};
const upload = (idx, file) => {
let _progressInfos = [...progressInfosRef.current.val];
return UploadService.upload(file, (event) => {
_progressInfos[idx].percentage = Math.round(
(100 * event.loaded) / event.total
);
setProgressInfos({ val: _progressInfos });
})
.then(() => {
toast.info(file.name + " Uploaded")
setMessage((prevMessage) => ([
...prevMessage,
"Uploaded the file successfully: " + file.name,
]));
})
.catch(() => {
_progressInfos[idx].percentage = 0;
setProgressInfos({ val: _progressInfos });
setMessage((prevMessage) => ([
...prevMessage,
"Could not upload the file: " + file.name,
]));
});
};
const uploadFiles = () => {
const files = Array.from(selectedFiles);
let _progressInfos = files.map(file => ({ percentage: 0, fileName: file.name }));
progressInfosRef.current = {
val: _progressInfos,
}
const uploadPromises = files.map((file, i) => upload(i, file));
Promise.all(uploadPromises)
.then(() => UploadService.getFiles())
.then((files) => {
setFileInfos(files.data);
});
setMessage([]);
};
return (
<div>
{progressInfos && progressInfos.val.length > 0 &&
progressInfos.val.map((progressInfo, index) => (
<div className="mb-2" key={index}>
<span>{progressInfo.fileName}</span>
<div className="progress">
<div
className="progress-bar progress-bar-info"
role="progressbar"
aria-valuenow={progressInfo.percentage}
aria-valuemin="0"
aria-valuemax="100"
style={{ width: progressInfo.percentage + "%" }}
>
{progressInfo.percentage}%
</div>
</div>
</div>
))}
<div className="row my-3">
<div className="col-8">
<label className="btn btn-default p-0">
<input type="file" multiple onChange={selectFiles} />
</label>
</div>
<div className="col-4">
<button
className="btn btn-success btn-sm"
disabled={!selectedFiles}
onClick={uploadFiles}
>
Upload
</button>
</div>
</div>
{message.length > 0 && (
<div className="alert alert-secondary" role="alert">
<ul>
{message.map((item, i) => {
return <li key={i}>{item}</li>;
})}
</ul>
</div>
)}
<div className="card">
{/* <div className="card-header">List of Files</div> */}
<ul className="list-group list-group-flush">
{!fileInfos &&
fileInfos.map((file, index) => (
<li className="list-group-item" key={index}>
{/* <a href={file.url}>{file.name}</a> */}
</li>
))}
</ul>
</div>
<ToastContainer position="top-center" autoClose={1000}/>
</div>
);
};
export default UploadFiles;
...
I'm not quite sure I understand your question perfectly, but do you want to pass down the changefileNameHandler function as a prop to your FilesUpload functional component?
In this case you can just add props as a paremeter:
const UploadFiles = (props) => { ...
and call it wherever you need it:
props.onUpdate(event)

Unable to pass props from parent to child and save it in state of child component

I'm trying to develop a website for fetching GitHub data, but I'm having problem in updating the component that shows data Formdata component. It doesn't seem to be updating form some reasons.
App:
export default class App extends Component {
constructor(props){
super(props);
this.state = {
uname:'',
udata:'',
};
this.handleInput = this.handleInput.bind(this);
this.getUser = this.getUser.bind(this);
}
getUser(){
fetch(`https://api.github.com/users/${this.state.uname}`)
.then(response => response.json())
.then(data => this.setState({udata:data}))
.catch(error => console.error(error));
}
handleInput(event){
this.setState({
uname:event.target.value
});
}
render() {
return (
<div>
<Header></Header>
<Form handleInput={this.handleInput} uname={this.state.uname} getUser={this.getUser}></Form>
<Formdata udata={this.state.udata}></Formdata>
</div>
)
}
}
Form:
export default function Form(props) {
const {getUser, handleInput, uname} = props;
return (
<div className="form">
<input className="textbar" placeholder="Search for username" value={uname} onChange={handleInput} name="uname"></input>
<button className="button" onClick={getUser} >Search</button>
</div>
)
}
Formdata:
export default class Formdata extends Component {
constructor(props){
super(props);
this.state = {
follower:'',
following:'',
public_repos:'',
visit_page:'',
avatar:''
}
this.updateUser = this.updateUser.bind(this);
};
componentDidMount(props){
this.updateUser();
}
updateUser(){
this.setState({follower:this.props.udata.followers});
this.setState({following:this.props.udata.following});
this.setState({public_repos:this.props.udata.public_repos});
this.setState({visit_page:this.props.udata.url});
this.setState({avatar:this.props.udata.avatar_url});
console.log(this.props.udata);
}
render() {
return (
<div>
<img className="imge" src= {this.state.avatar} alt=" "></img>
<div className="details">
<div className="compon">Followers: {this.state.followers}</div>
<div className="compon">Following: {this.state.following}</div>
<div className="compon">public repos" {this.state.public_repos}</div>
</div>
<div className="urls">Page:{this.state.visit_page}</div>
</div>
)
}
}
I can't figure out how to update component Formdata on clicking search button in Form component.
Full Working App: StackBlitz
import React, { Component, useEffect } from "react";
import "./style.css";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
uname: "",
udata: ""
};
this.handleInput = this.handleInput.bind(this);
this.getUser = this.getUser.bind(this);
}
getUser() {
fetch(`https://api.github.com/users/${this.state.uname}`)
.then(response => response.json())
.then(data =>
this.setState({ udata: data }, () => {
console.log(this.state.udata);
})
)
.catch(error => console.error(error));
}
handleInput(event) {
this.setState(
{
uname: event.target.value
},
() => {
console.log(this.state.uname);
}
);
}
render() {
return (
<div>
<Form
handleInput={this.handleInput}
uname={this.state.uname}
getUser={this.getUser}
/>
<Formdata udata={this.state.udata} />
</div>
);
}
}
const Form = props => {
const { getUser, handleInput, uname } = props;
return (
<div className="form">
<input
className="textbar"
placeholder="Search for username"
value={uname}
onChange={handleInput}
name="uname"
/>
<button className="button" onClick={getUser}>
Search
</button>
</div>
);
};
const Formdata = ({ udata }) => {
useEffect(() => {
console.log(JSON.stringify(udata.login));
}, [udata]);
return (
<div style={styles.card}>
{udata.login ? (
<div style={styles.cardImg}>
<div>
<img
style={styles.img}
className="imge"
src={udata?.avatar_url}
alt=" "
/>
</div>
<div className="details">
<div className="compon">Followers: {udata?.followers}</div>
<div className="compon">Following: {udata?.following}</div>
<div className="compon">Public repos: {udata?.public_repos}</div>
<div className="urls">Page: {udata?.url}</div>
</div>
</div>
) : (
<div>
<p>No Data Available</p>
</div>
)}
</div>
);
};
const styles = {
card: {
display: "flex",
flex: 1,
backgroundColor: "rgba(21,21,21,0.2)",
padding: 10,
marginTop: 10,
borderRadius: 5
},
cardImg: {
display: "flex",
flex: 1,
flexDirection: "row",
flexWrap: "wrap",
overflow: "hidden",
textOverflow: "ellipsis",
color: "rgba(0,0,0,0.7)"
},
img: {
marginRight: 10,
width: 100,
height: 100,
borderRadius: 10,
overflow: "hidden"
}
};
Do not copy props into state, use the props directly in your JSX:
div>
<img className="imge" src= {this.props.udata.avatar} alt=" "></img>
<div className="details">
<div className="compon">Followers: {this.props.udata.followers}</div>
<div className="compon">Following: {this.props.udata.following}</div>
<div className="compon">public repos" {this.props.udata.public_repos}</div>
</div>
<div className="urls">Page:{this.props.udata.visit_page}</div>
</div>
If you copy props into state, you are creating redundant copy of props and it is difficult to keep props and state in sync. And it is a React anti-pattern.
Just make sure this.props.udata is not undefined, it is ok if it is empty object {}. If it is undefined, put a check / conditional rendering.
anti-pattern-unconditionally-copying-props-to-state
Formdata.updateUser() isn't being called at any point. You probably just need to call it in componentDidMount():
export default class Formdata extends Component {
...
componentDidMount(props){
this.updateUser();
}
updateUser(){
this.setState({follower:this.props.udata.followers});
this.setState({following:this.props.udata.following});
this.setState({public_repos:this.props.udata.public_repos});
this.setState({visit_page:this.props.udata.url});
this.setState({avatar:this.props.udata.avatar_url});
console.log(this.props.udata);
}
...
}

Reactjs Update the state without reload the page

I built a shopping cart in my app. This is the flow:
- The customer sees a list of items and clicks on one he wants;
- The next page is where he chooses the quantity of products and then I save in localStorage;
- By clicking Confirm, he goes to the shopping cart with the same products he has chosen. On this page (shopping cart) he can change the quantity and in this moment the total and quantity must change (see the image).
I was able to do this reloading the page, but when in production the page is not working properly. I need to do this without reload the page.
How to do this without reload the page?
I got 3 components: Header (with the icon cart), ChooseQuantity and Cart.
See below the code:
//My Choose Quantity Component
import React from 'react';
import '../../components/ChooseQuantity/ChooseQuantity.css';
class ChooseQuantity extends React.Component {
constructor(props) {
super(props);
const { lotQuantity, totalQuantity, maxTotalItems, maxPurchase, lot, totalTickets, events, onChange } = this.props;
this.state = {
counter: 0,
lotQuantity: lotQuantity,
totalQuantity: totalQuantity,
maxTotalItems: maxTotalItems,
maxPurchase: maxPurchase,
totalTickets: totalTickets,
onChange: onChange,
events: events,
lot: lot,
tickets: events.tickets,
cart: []
}
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
componentDidMount() {
// console.log(this.state.lot);
// localStorage.setItem('teste', JSON.stringify(this.state.lotQuantity));
}
// static getDerivedStateFromProps(props, state) {
// console.log(props.lot);
// // console.log(state);
// if (props.selected !== state.selected) {
// return {
// selected: props.selected,
// };
// }
// }
// componentDidUpdate(prevProps, prevState) {
// console.log(this.props.lot);
// localStorage.setItem('teste', JSON.stringify(this.props.lot.quantity));
// if (this.props.lot !== prevProps.lot) {
// // this.selectNew();
// }
// }
async increment() {
await this.setState({
lotQuantity: this.state.lotQuantity + 1,
totalQuantity: + 1,
});
let lotUniqueNumber = this.state.lot.lotUniqueNumber;
let lotQuantity = this.state.lotQuantity;
var ar_lot = [];
this.state.tickets.lot.forEach(function (item) {
if (lotUniqueNumber === item.lotUniqueNumber) {
item.quantity = lotQuantity;
item.total = item.totalLotPrice * item.quantity
}
ar_lot.push(item);
})
// console.log(ar_lot);
//CALCULATING A QUANTITY
var ob_qtd = ar_lot.reduce(function (prevVal, elem) {
const ob_qtd = prevVal + elem.quantity;
return ob_qtd;
}, 0);
await this.setState({ totalTickets: ob_qtd })
//CALCULATING A QUANTITY
//CALCULATING THE TOTAL
var ob_total = ar_lot.reduce(function (prevVal, elem) {
const ob_total = prevVal + elem.total;
return ob_total;
}, 0);
// CALCULATING THE TOTAL
//RIDING THE SHOPPING CART
let total = {
price: ob_total,
totalQuantity: ob_qtd,
};
let tickets = {
name: this.state.tickets.name,
prevenda: this.state.tickets.prevenda,
unique_number: this.state.tickets.unique_number,
lot: ar_lot
}
let events = {
banner_app: this.state.events.banner_app,
installments: this.state.events.installments,
max_purchase: this.state.events.max_purchase,
name: this.state.events.name,
tickets: tickets
}
var cart = { events: events, total: total };
this.setState({
cart: cart
})
// console.log(cart);
localStorage.setItem('cart', JSON.stringify(cart));//RECORDING CART IN LOCALSTORAGE
localStorage.setItem('qtd', JSON.stringify(ob_qtd));
window.location.reload();//UPDATE PAGE FOR CHANGES TO BE UPDATED
}
async decrement() {
await this.setState({
lotQuantity: this.state.lotQuantity - 1,
totalQuantity: - 1,
totalTickets: this.state.totalTickets - 1,
});
let lotUniqueNumber = this.state.lot.lotUniqueNumber;
let lotQuantity = this.state.lotQuantity;
var ar_lot = [];
this.state.tickets.lot.forEach(function (item) {
if (lotUniqueNumber === item.lotUniqueNumber) {
item.quantity = lotQuantity;
item.total = item.totalLotPrice * item.quantity
}
ar_lot.push(item);
})
//CALCULANDO A QUANTIDADE
var ob_qtd = ar_lot.reduce(function (prevVal, elem) {
const ob_qtd = prevVal + elem.quantity;
return ob_qtd;
}, 0);
//CALCULANDO A QUANTIDADE
//CALCULANDO O TOTAL
var ob_total = ar_lot.reduce(function (prevVal, elem) {
const ob_total = prevVal + elem.total;
return ob_total;
}, 0);
//CALCULANDO O TOTAL
let total = {
price: ob_total,
totalQuantity: ob_qtd,
};
let tickets = {
name: this.state.tickets.name,
prevenda: this.state.tickets.prevenda,
unique_number: this.state.tickets.unique_number,
lot: ar_lot
}
let events = {
banner_app: this.state.events.banner_app,
installments: this.state.events.installments,
max_purchase: this.state.events.max_purchase,
name: this.state.events.name,
tickets: tickets
}
var cart = { events: events, total: total };
localStorage.setItem('cart', JSON.stringify(cart));
localStorage.setItem('qtd', JSON.stringify(ob_qtd));
window.location.reload();
}
render() {
return (
<div className="choose-quantity">
{
this.state.lotQuantity <= 0 ?
<div className="space-button"></div> :
<button className='minus' onClick={this.decrement}><i className="fas fa-minus"></i></button>
}
<div id='counter' className="qtd" value={this.state.lotQuantity} onChange={this.onChange}>{this.state.lotQuantity}</div>
{
this.state.totalTickets >= this.state.maxPurchase ?
<div className="space-button"></div> :
<button className="plus" onClick={() => this.increment(this.state.lotQuantity)}><i className="fas fa-plus"></i></button>
}
</div>
)
}
}
export default ChooseQuantity;
//My Shopping Cart Component
import React, { Component } from 'react';
import Swal from "sweetalert2";
import { Link } from 'react-router-dom';
import './Cart.css';
import '../../components/Css/App.css';
import Lot from './Lot';
import ChooseQuantity from './ChooseQuantity';
import Header from '../../components/Header/Header';
import Tabbar from '../../components/Tabbar/Tabbar';
const separator = '/';
class Cart extends Component {
constructor(props) {
super(props);
this.state = {}
this.choosePayment = this.choosePayment.bind(this);
}
async componentDidMount() {
const company_info = JSON.parse(localStorage.getItem('company_info'));
await this.setState({
company_image: company_info.imagem,
company_hash: company_info.numeroUnico,
})
const cart = JSON.parse(localStorage.getItem('cart'));
const total = cart.total;
if(cart){
const {
events,
events: { tickets },
total
} = cart;
await this.setState({
cart,
events,
tickets: tickets,
banner_app: events.banner_app,
eventName: cart.events.name,
priceTotal: total.price,
quantity: total.totalQuantity,
lots: tickets.lot,
maxTotalItems: cart.events.max_purchase,
selectedLots: tickets.lot,
total: total.totalQuantity
});
}
const teste = JSON.parse(localStorage.getItem('teste'))
this.setState({teste: teste})
}
choosePayment() {
Swal.fire({
title: 'Método de Pagamento',
text: 'Qual o médtodo de pagamento que você deseja usar?',
confirmButtonText: 'Cartão de Crédito',
confirmButtonColor: '#007bff',
showCancelButton: true,
cancelButtonText: 'Boleto Bancário',
cancelButtonColor: '#007bff',
}).then((result) => {
if (result.value) {
this.props.history.push('/checkout');
} else{
this.props.history.push('/checkout-bank-slip');
}
})
}
render() {
return (
<div>
<Header Title="Carrinho" ToPage="/" />
{
this.state.total <= 0 ?
<Tabbar />
:
null
}
<div className="cart">
<div className="container-fluid">
{
this.state.total > 0 ?
<div>
<div className="box-price">
<div className="row box-default ">
<div className="col col-price">
<h6>{this.state.quantity} INGRESSO{this.state.quantity > 1 ? 'S' : ''}</h6>
<h5>R$ {parseFloat(this.state.priceTotal).toFixed(2).replace('.', ',')}</h5>
</div>
</div>
</div>
<div className="row">
<div className="col-12 col-image no-padding">
<img src={this.state.banner_app} alt="" />
</div>
</div>
<div className="row">
<div className="col">
<h1 className="event-title text-center">{this.state.eventName}</h1>
</div>
</div>
<div className="padding-15">
{
this.state.lots.map((lot, l) =>
<div key={l}>
{
lot.quantity > 0 ?
<div>
<div className="row">
<div className="col">
<h5 className="ticket-name">{lot.ticketName}</h5>
</div>
</div>
<div className="row">
<div className="col-8">
<h5 className="lot-name">
{ lot.lotName } - ({lot.lotNumber}º Lote)
</h5>
<h6 className="lot-price">
R$ {lot.lotPrice.replace('.', ',')} ({lot.lotPrice.replace('.', ',')} + {lot.lotPriceTax.replace('.', ',')})
</h6>
</div>
<div className="col-4">
<h3 className='lot-big-price'>
{lot.lotPrice.replace('.', ',')}
</h3>
</div>
</div>
<div className="row">
<div className="col align-items">
<ChooseQuantity
lotQuantity={lot.quantity}
maxPurchase={this.state.events.max_purchase}
totalTickets={this.state.total}
lot={lot}
events={this.state.events}
maxTotalItems={this.state.maxTotalItems}
onCLick={this.onClick}
/>
</div>
</div>
</div>
:
null
}
</div>
)
}
<div className="row cart-footer" style={{ marginRight: '-15px', marginLeft: '-15px', backgroundColor: '#f4f7fa' }}>
<button className="col col-purchase" style={{ justifyContent: 'center', alignItems: 'center' }} onClick={this.choosePayment}>
Confirmar Comprar
</button>
</div>
</div>
</div>
:
<div className='padding-15'>
<div className="mt-5 no-margin box-default row">
<div className="col">
<h3 className="text-center">
Não há nenhum item em seu carrinho.
</h3>
<p className="text-center">
Toque no botão <strong>Buscar Eventos</strong> para iniciar uma nova pesquisa.
</p>
<Link className="btn btn-primary btn-block" to="/">
Buscar Eventos
</Link>
</div>
</div>
<div className="row no-margin box-default mt-3">
<div className="col">
<img src={`//www.yeapps.com.br/admin/files/empresa/${this.state.company_hash}/${this.state.company_image}`} alt={`${this.state.company_name}`} />
</div>
</div>
</div>
}
</div>
</div>
</div>
);
}
}
export default Cart;
//My Header Component
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
// import { withRouter } from 'react-router';
import './Header.css';
import BackButton from '../BackButton/BackButton';
class Header extends Component {
constructor(props){
super(props);
this.state = {
qtd: 0
}
}
componentDidMount() {
const qtd = JSON.parse(localStorage.getItem('qtd'));
this.setState({qtd: qtd});
}
render() {
const { Title } = this.props;
return (
<div>
<nav className="navbar">
{ this.props.Title === 'Home' ? null : <BackButton />}
<Link to="/cart" className="icon-cart">
<i className="fas fa-shopping-cart"></i>
<span className="badge badge-danger">
{this.state.qtd}
</span>
</Link>
<div className="navbar-brand">
{Title}
</div>
</nav>
</div>
);
}
}
export default Header;
You need to update your presentational components(Card,Header) state in componentDidUpdate the same way as you did it in componentDidMount. componentDidMount works only one time with first render and there you set your state variables which you use in render. Do the same setState in componentDidUpdate
P.S. but you need to be sure that with counter increasing your presentational components get new props which will trigger componentDidUpdate.In your case you use localStorage but it will be better to pass data through common parent container(the one which holds those 3 components) as props to Card and Header.

Passing the value of page from Pagination.jsx to App.jsx

While I'm trying to get in to React, I started a project and got stuck. Maybe some one can help me to find the issue. Bellow I explain what the app should do.
The user types a query in an input-box inside SearchBar.jsx
The SearchBar component passes the query to App.jsx and fires up fetchPhotos function, which starts an API request.
To sort out pagination, the App.jsx imports Pagination.jsx, which calculates the number of pictures in the response and displays pagination buttons.
The above works.
But now if you click on a pagination button, the value for page from Pagination component should be passed to App.jsx and so to fetchPhotos function (runs the API request).
I guess the problem is that the value of page never finds its way to App.jsx and so the API request is missing the value of page.
I spent hours but couldn't find a way to fix it, due to lack of knowledge. Could you please point me to the right direction and show me what is wrong with the code?
App.jsx
import React, { Component } from "react";
import axios from "axios";
import Pagination from "../Pagination";
import SearchBar from "../SearchBar";
import ListItem from "../ListItem";
import "./app.scss";
class App extends Component {
state = {
photos: [],
totalPhotos: 0,
perPage: 30,
currentPage: 1,
query: null
};
componentDidMount() {
this.fetchPhotos("gorilla", this.state.currentPage);
}
fetchPhotos = (inputValue, page) => {
const baseUrl = "https://api.unsplash.com/search/photos";
const options = {
headers: {
Authorization: `Client-ID ${process.env.REACT_APP_UNSPLASH_API_KEY}`
},
params: {
query: inputValue,
page: this.state.page,
per_page: this.state.perPage
}
};
axios
.get(baseUrl, options)
.then(response => {
this.setState({
photos: response.data.results,
totalPhotos: parseInt(response.headers["x-total"]),
currentPage: page,
query: inputValue
});
})
.catch(() => {
console.log("Error");
});
};
render() {
return (
<div className="app">
<SearchBar onSubmit={this.fetchPhotos} />
<Pagination
current={this.state.currentPage}
total={this.state.totalPhotos}
perPage={this.state.perPage}
query={this.state.query}
onPageChanged={query => this.fetchPhotos(this.state.query)}
/>
<List data={this.state.photos} />
</div>
);
}
}
const List = ({ data }) => {
var items = data.map(photo => <ListItem key={photo.id} photo={photo} />);
return <div className="grid">{items}</div>;
};
export default App;
SearchBar.jsx
import React, { Component } from "react";
class SearchBar extends Component {
state = {
inputValue: ""
};
handleFormSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state.inputValue);
};
render() {
return (
<div className="header">
<h1>Search for images on Unsplash</h1>
<form onSubmit={this.handleFormSubmit} className="ui form">
<input
type="text"
placeholder="Type here to search for images"
value={this.state.inputValue}
onChange={e => this.setState({ inputValue: e.target.value })}
/>
</form>
</div>
);
}
}
export default SearchBar;
Pagination.jsx
import React, { Component } from "react";
class Pagination extends Component {
pages() {
var pages = [];
for (var i = this.rangeStart(); i <= this.rangeEnd(); i++) {
pages.push(i);
}
return pages;
}
rangeStart() {
var start = this.props.current - this.props.pageRange;
return start > 0 ? start : 1;
}
rangeEnd() {
var end = this.props.current + this.props.pageRange;
var totalPages = this.totalPages();
return end < totalPages ? end : totalPages;
}
totalPages() {
return Math.ceil(this.props.total / this.props.perPage);
}
nextPage() {
return this.props.current + 1;
}
prevPage() {
return this.props.current - 1;
}
hasFirst() {
return this.rangeStart() !== 1;
}
hasLast() {
return this.rangeEnd() < this.totalPages();
}
hasPrev() {
return this.props.current > 1;
}
hasNext() {
return this.props.current < this.totalPages();
}
changePage(page) {
this.props.onPageChanged(page);
console.log("Page inside Pagination", page);
}
render() {
return (
<div className="pagination">
<div className="pagination__left">
<span
role="button"
className={!this.hasPrev() ? "hidden" : ""}
onClick={e => this.changePage(this.prevPage())}
>
Prev
</span>
</div>
<div className="pagination__mid">
<ul>
<li className={!this.hasFirst() ? "hidden" : ""}>
<span role="button" onClick={e => this.changePage(1)}>
1
</span>
</li>
<li className={!this.hasFirst() ? "hidden" : ""}>...</li>
{this.pages().map((page, index) => {
return (
<li key={index}>
<span
role="button"
onClick={e => this.changePage(page)}
className={this.props.current === page ? "current" : ""}
>
{page}
</span>
</li>
);
})}
<li className={!this.hasLast() ? "hidden" : ""}>...</li>
<li className={!this.hasLast() ? "hidden" : ""}>
<span
role="button"
onClick={e => this.changePage(this.totalPages())}
>
{this.totalPages()}
</span>
</li>
</ul>
</div>
<div className="pagination__right">
<span
className={!this.hasNext() ? "hidden" : ""}
onClick={e => this.changePage(this.nextPage())}
>
Next
</span>
</div>
</div>
);
}
}
Pagination.defaultProps = {
pageRange: 2
};
export default Pagination;
I think your error is at `onChange', because you are giving current state query to fetch instead of the new query:
onPageChanged={query => this.fetchPhotos(this.state.query)}
You should replace it for new query like:
onPageChanged={query => this.fetchPhotos(query)}
EDIT 1:
You can see working it on https://codesandbox.io/s/9ymjj8ko9p?fontsize=14.
The changes is just as I said, on App.jsx:
params fixed passing page from function params and not from
fix onPageChange prop to Pagination like:
onPageChanged={page => this.fetchPhotos(this.state.query, page)}

Resources