I present my problem to you
with the following code I make a payment,
I would like that if the payment is refused I redirect to a page otherwise if the payment is accept I redirect to another page
do you have any idea how to do this?
I tried to simplify the code so that it is as clear as possible
Thanks for your help Neff
import { Redirect } from 'react-router-dom';
import React, { Component } from 'react';
import { CardNumberElement, CardExpiryElement, CardCVCElement, injectStripe } from 'react-
stripe-elements';
import { CardText, Col, Container, CardTitle, Button, Input, Card } from 'reactstrap';
import './Payment.scss'
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT + '/api';
class _Form extends Component {
constructor(props) {
super(props);
this.state = {
alertMessage: '',
alertStyle: '',
randomID: null,
redirect: false,
loading: false,
};
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
postbackend = () => {
const newItems = this.props.items.map((item) => {
const { title, quantity, } = item;
return {
title,
quantity,
};
});
const config = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ ...this.state, items: newItems }),
};
const url = entrypoint + "/alluserpls";
fetch(url, config)
.then(res => res.json())
.then(res => {
if (res.error) {
alert(res.error);
} else {
alert(`film ajouté avec l'ID ${res}!`);
}
}).catch(e => {
console.error(e);
}).finally(() => this.setState({ redirect: true }));
}
handleSubmit = async (ev) => {
ev.preventDefault();
this.setState({ loading: true });
this.props.stripe.createToken({ name: this.props.clientName })
.then(result => {
if (typeof (result.error) !== 'undefined') {
this.setState({ error: result.error.message, success: '' });
this.postbackend();
} else {
this.stripeCreateCharge(result.token, this.props.totalPrice).then(() => {
this.postbackend();
});
}
});
}
stripeCreateCharge(token, amount) {
const params = { token: token.id, amount: amount };
const qParams = queryString.stringify(params);
const url = [entrypoint + "/stripe", qParams].join('?');
return fetch(url)
.then(response => response.json())
.then(val => {
if (val.ok) {
return val.message;
} else {
throw val.message;
}
})
.then(success => {
this.setState({ alertMessage: success, alertStyle: 'success' });
return Promise.resolve()
})
.catch(error => this.setState({ alertMessage: error, alertStyle: 'danger' }));
}
render() {
const { loading } = this.state;
const redirect = this.state.redirect;
if (redirect) {
return <Redirect to="/OderSummaryScreen" />
}
else {
return (
<div >
<form method="post" onSubmit={(ev) => this.handleSubmit(ev)}>
<Container>
<CardTitle className='cardTitlePaymentFormComponent'>Entre ton moyen de paiement</CardTitle>
<Card className="CardPaymenntFormComponenentCard">
{/* <Alert msg={this.state.alertMessage} style={this.state.alertStyle} /> */}
<div className="col12PaymentFormComponent">
<div className="col8PaymentFormComponent">
<CardText className='justOnlyForFunckingPaddindOnTextForPaymentFormComponent'>Numéro de Carte</CardText>
<CardNumberElement className="cardNumberElementPaymentFormComponent" />
</div>
<div className="col4PaymentFormComponent">
<CardText className='justOnlyForFunckingPaddindOnTextForPaymentFormComponent'> Expiration</CardText>
<CardExpiryElement className="cardExpiryElementPaymentFormComponent" />
</div>
</div>
<div className="col12PaymentFormComponent">
<div className="col8PaymentFormComponent">
<ClienInfo />
</div>
<div className="col4PaymentFormComponent">
<CardText className='justOnlyForFunckingPaddindOnTextForPaymentFormComponent'>Cryptogramme</CardText>
<CardCVCElement className="cardCVCElementPaymentFormComponent" />
</div>
</div>
</Card>
</Container>
<Container className='containerPaymentFooterContainer' >
<Col sm="12" className='col12PaymentsFooter'>
<Input onClick={this.handleClick} type="checkbox" required className="inputCheckboxPaymentsFooter" />{' '}
<CardText className='cardTextPaymentsFooter' > Je reconnais avoir lu et pris connaissance des Termes, de la Charte de Confidentialité et des CGU, et les accepte.</CardText>
</Col>
</Container>
<Col sm="12" className='col12PaymentsFooterButtonCol12' >
{!loading && <div >
<Button className='buttonPaymentsFooterbutton' type="submit" value="Envoyer" disabled={loading} >
<CardTitle className="buttonPaymentsCardTitleFooter">PAYER </CardTitle>
<CardTitle className="buttonPaymentsCardTitleFooter" >{this.props.total} € </CardTitle>
</Button>
</div>}
{loading && <div className="wtfloadingLogoHandeSpinerBro" ><Handespiner /> </div>}
</Col>
</Form>
)}
</Formik>
</Col>
</Container>
</form>
</div>
)
};
}
}
const mapStateToProps = (state) => {
return {
items: state.addedItems,
}
}
export default connect(mapStateToProps)(injectStripe(_Form))
In your postbackend function you can update your fetch request like this:
fetch(url, config)
.then(res => res.json())
.then(res => {
if (res.error) {
alert(res.error);
this.props.history.replace("/"); // Your Error Page
} else {
alert(`film ajouté avec l'ID ${res}!`);
this.props.history.push("/"); // Your Success Page
}
}).catch(e => {
console.error(e);
this.props.history.replace("/"); // Your Error Page
}).finally(() => this.setState({
redirect: true
}));
The catch case will be invoked if case of any error. While your else part will take care of unsuccessful response from the server.
Related
The below code renders a list of planets (from this API: https://swapi.dev/).I'm trying to find a way to make my planets clickable. When a specific planet is clicked in the list, it should open up a detail page with specific info (pulled from API) on the planet that has been clicked. Can I do this with or ? What is the best practice way to do this?
import React, { PureComponent } from 'react'
import axios from "axios";
class Home extends PureComponent {
constructor(props) {
super(props)
this.state = {
planets: [],
filteredPlanets: []
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(e){ // eslint-disable-next-line
let planetssearchlist = this.state.planets.filter(planet => {
if(planet.name){
if(planet.name.toLowerCase().includes(e.target.value.toLowerCase())){
return true
}
}
})
this.setState({
filteredPlanets: planetssearchlist
})
}
componentDidMount(){
axios({
method: "GET",
url: "https://swapi.dev/api/planets/"
})
.then(response => {
console.log(response)
let planetslist = response.data.results;
this.setState({planets: planetslist, filteredPlanets: planetslist})
})
.catch(error => {
console.log("You've made an error with the planets load charles: ",error)
})
}
render() {
return (
<div>
<h1>Star Wars Planets</h1>
<form>
<input placeholder="searchbar" type="text" onChange={this.handleChange}></input>
</form>
{
this.state.filteredPlanets.map((planet,i) => (
<p key={i}>{planet.name}</p>
))
}
</div>
)
}
}
export default Home
Add a new function to get the planet info. In your map add onClick event handler to get the planet info for the clicked planet.
Add two new variable to your state
this.state = {
planets: [],
filteredPlanets: [],
planetInfo: {},
isGettingPlanetInfo: false,
};
Add a function to get the planet info
getPlanetInfo = url => {
this.setState({
isGettingPlanetInfo: true
})
axios({
method: "GET",
url: url
})
.then(response => {
console.log(response.data)
this.setState({
planetInfo: response.data,
isGettingPlanetInfo: false,
})
})
.catch(error => {
this.setState({
isGettingPlanetInfo: false
})
console.log(
"You've made an error with the planets load charles: ",
error
);
});
};
Add on click event handler to the planet
{this.state.filteredPlanets.map((planet, i) => (
<p onClick={() => this.getPlanetInfo(planet.url)} key={i}>
{planet.name}
</p>
))}
Home component
import React, { PureComponent } from 'react';
import axios from 'axios';
export default class Home extends PureComponent {
constructor(props) {
super(props);
this.state = {
planets: [],
filteredPlanets: [],
planetInfo: {},
isGettingPlanetInfo: false,
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
// eslint-disable-next-line
let planetssearchlist = this.state.planets.filter((planet) => {
if (planet.name) {
if (planet.name.toLowerCase().includes(e.target.value.toLowerCase())) {
return true;
}
}
});
this.setState({
filteredPlanets: planetssearchlist,
});
}
getPlanetInfo = (url) => {
this.setState({
isGettingPlanetInfo: true,
});
axios({
method: 'GET',
url: url,
})
.then((response) => {
console.log(response.data);
this.setState({
planetInfo: response.data,
isGettingPlanetInfo: false,
});
})
.catch((error) => {
this.setState({
isGettingPlanetInfo: false,
});
console.log(
"You've made an error with the planets load charles: ",
error
);
});
};
componentDidMount() {
axios({
method: 'GET',
url: 'https://swapi.dev/api/planets/',
})
.then((response) => {
console.log(response);
let planetslist = response.data.results;
this.setState({ planets: planetslist, filteredPlanets: planetslist });
})
.catch((error) => {
console.log(
"You've made an error with the planets load charles: ",
error
);
});
}
render() {
return (
<div>
<h1>Star Wars Planets</h1>
<form>
<input
placeholder='searchbar'
type='text'
onChange={this.handleChange}
/>
</form>
{this.state.filteredPlanets.map((planet, i) => (
<p onClick={() => this.getPlanetInfo(planet.url)} key={i}>
{planet.name}
</p>
))}
<hr />
{this.state.isGettingPlanetInfo ? (
<p>getting planet info...</p>
) : typeof this.state.planetInfo === 'object' &&
Object.keys(this.state.planetInfo).length ? (
<div>
<p>name: {this.state.planetInfo.name}</p>
<p>climate: {this.state.planetInfo.climate}</p>
<p>population: {this.state.planetInfo.population}</p>
</div>
) : (
''
)}
</div>
);
}
}
With react router
import React, { PureComponent } from "react";
import axios from "axios";
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
class PlanetInfo extends React.Component {
state = {
url: "",
planetInfo: {},
isGettingPlanetInfo: false
};
getPlanetInfo = () => {
this.setState({
isGettingPlanetInfo: true
});
axios({
method: "GET",
url: this.state.url
})
.then(response => {
console.log(response.data);
this.setState({
planetInfo: response.data,
isGettingPlanetInfo: false
});
})
.catch(error => {
this.setState({
isGettingPlanetInfo: false
});
console.log(
"You've made an error with the planets load charles: ",
error
);
});
};
componentDidMount = () => {
this.setState(
{
url: this.props.location.state.planet.url
},
this.getPlanetInfo
);
};
render() {
return (
<div>
{this.state.isGettingPlanetInfo ? (
<p>getting planet info...</p>
) : typeof this.state.planetInfo === "object" &&
Object.keys(this.state.planetInfo).length ? (
<div>
<p>name: {this.state.planetInfo.name}</p>
<p>climate: {this.state.planetInfo.climate}</p>
<p>population: {this.state.planetInfo.population}</p>
</div>
) : (
""
)}
</div>
);
}
}
class Home extends PureComponent {
constructor(props) {
super(props);
this.state = {
planets: [],
filteredPlanets: []
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
// eslint-disable-next-line
let planetssearchlist = this.state.planets.filter(planet => {
if (planet.name) {
if (planet.name.toLowerCase().includes(e.target.value.toLowerCase())) {
return true;
}
}
});
this.setState({
filteredPlanets: planetssearchlist
});
}
componentDidMount() {
axios({
method: "GET",
url: "https://swapi.dev/api/planets/"
})
.then(response => {
console.log(response);
let planetslist = response.data.results;
this.setState({ planets: planetslist, filteredPlanets: planetslist });
})
.catch(error => {
console.log(
"You've made an error with the planets load charles: ",
error
);
});
}
render() {
return (
<div>
<h1>Star Wars Planets</h1>
<form>
<input
placeholder="searchbar"
type="text"
onChange={this.handleChange}
/>
</form>
{this.state.filteredPlanets.map((planet, i) => (
<Link to={{ pathname: "/info", state: { planet: planet } }}>
<p key={i}>{planet.name}</p>
</Link>
))}
</div>
);
}
}
export default function Navigation() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/info" component={PlanetInfo} />
</Switch>
</BrowserRouter>
);
}
I am getting the error
Error: Function components cannot have refs. Did you mean to use
React.forwardRef()?
And if you check my code it is correct. I am also passing even and it says
Line 91: Unexpected use of 'event' no-restricted-globals
If I am doing something wrong I do not know. According to my research I found that there is some version issue maybe or not. I am using the latest version of create react app
import React, { Component } from 'react';
import { AuthUserContext } from '../Session';
import { withFirebase } from '../Firebase';
import NewsList from './NewsList';
import { ValidatorForm, TextValidator } from 'react-material-ui-form-validator';
import { Container, Card, CardContent, Button } from '#material-ui/core';
import Form from 'react-bootstrap/Form';
class News extends Component {
constructor(props) {
super(props);
this.state = {
newsTitle: '',
newsDescription: '',
news: [],
limit: 5,
loading: false,
submitted: false,
error: null,
};
}
componentDidMount() {
this.onListenForNews();
}
onListenForNews = () => {
this.setState({ loading: true });
this.props.firebase
.news()
.orderByChild('createdAt')
.limitToLast(this.state.limit)
.on('value', snapshot => {
const newsObject = snapshot.val();
if (newsObject) {
const newsLis = Object.keys(newsObject).map(key => ({
...newsObject[key],
uid: key,
}));
this.setState({
news: newsLis,
loading: false,
});
} else {
this.setState({ news: null, loading: false });
}
});
};
componentWillUnmount() {
this.props.firebase.news().off();
}
handleChange = (event) => {
this.setState({ [event.target.name]: event.target.value });
};
onCreateNews = (event, authUser) => {
this.props.firebase.news().push({
newsTitle: this.state.newsTitle,
newsDescription: this.state.newsDescription,
userId: authUser.uid,
createdAt: this.props.firebase.serverValue.TIMESTAMP,
});
this.setState({
newsTitle: '',
newsDescription: '',
error: null,
submitted: true,
});
event.preventDefault();
};
onEditNews = (news, newsTitle, newsDescription) => {
this.props.firebase.news(news.uid).set({
...news,
newsTitle,
newsDescription,
editedAt: this.props.firebase.serverValue.TIMESTAMP,
});
};
onRemoveNews = uid => {
this.props.firebase.news(uid).remove();
};
onNextPage = () => {
this.setState(
state => ({ limit: state.limit + 5 }),
this.onListenForNews,
);
};
render() {
const { users } = this.props;
const { newsTitle, newsDescription, news, loading, submitted, error } = this.state;
return (
<AuthUserContext.Consumer>
{authUser => (
<div>
{!loading && news && (
<button type="button" onClick={this.onNextPage}>
More
</button>
)}
{loading && <div>Loading ...</div>}
{news && (
<NewsList
news={news.map(news => ({
...news,
user: users
? users[news.userId]
: { userId: news.userId },
}))}
onEditNews={this.onEditNews}
onRemoveNews={this.onRemoveNews}
/>
)}
{!news && <div>There are no messages ...</div>}
<Container maxWidth="lg">
<ValidatorForm
ref="form"
onSubmit={event =>
this.onCreateNews(event, authUser)
}
>
<div>
{error && (
<div className="alert alert-danger" role="alert">
{error.message}
</div>
)}
<Card>
<CardContent>
<Form.Group>
<TextValidator
label="News Title"
onChange={this.handleChange}
name="newsTitle"
type="text"
value={newsTitle}
variant="outlined"
fullWidth={true}
validators={['required']}
errorMessages={['New title field is required', 'News title is not valid']}
/>
</Form.Group>
<Form.Group>
<TextValidator
label="Description"
onChange={this.handleChange}
name="newsDescription"
type="text"
value={newsDescription}
variant="outlined"
fullWidth={true}
validators={['required']}
errorMessages={['Description field is required']}
/>
</Form.Group>
<Form.Group>
<Button
color="primary"
variant="contained"
type="submit"
fullWidth={true}
size="large"
disabled={submitted}
>
{
(submitted && 'Signing In - Redirecting')
|| (!submitted && 'Sign In')
}
</Button>
</Form.Group>
</CardContent>
</Card>
</div>
</ValidatorForm>
</Container>
</div>
)}
</AuthUserContext.Consumer>
);
}
}
export default withFirebase(News);
so I am currently working on an admin control panel for a page that displays a list of card. The cards contain details passed on from a local API that is hosted with a range of data such as "id", "title", "URL" and "ThumbnailUrl" and this is obtained from "localhost:0000/videos".
So I have a module, one that creates the Card called HelpCard.tsx and the code is as follows:
import React, { Component } from "react";
import "../help/HelpCardTwo.css";
import "../help/HelpList";
import Popup from "reactjs-popup";
import spinner from "../help/spinner.gif";
import placeholder from "../help/placeholder.jpeg";
interface Props {
id: string;
url: string;
title: string;
thumbnail: string;
deleteProduct: (id: any) => void;
editProduct: (id: any, title: string, url: string, thumbnail: string) => void;
}
interface State {
title: string;
thumbnail: string;
id: string;
url: string;
imageLoading?: boolean;
tooManyRequests?: boolean;
}
export default class HelpCard extends Component<Props, State> {
state = {
url: "",
id: "",
title: "",
imageLoading: true,
tooManyRequests: false,
thumbnail: ""
};
componentDidMount() {
const { url, title, thumbnail } = this.props;
const id = url.split("/")[url.split("/").length - 2];
this.setState({
url,
id,
title,
thumbnail,
imageLoading: true,
tooManyRequests: false
});
}
render() {
const isThumbnail = this.state.thumbnail;
const adminhelpcard = this.state;
return (
<React.Fragment>
<div className="cards">
<article className="card card--2">
<div className="card__info-hover"></div>
<div className="card__img">
{this.state.imageLoading ? <img src={placeholder} style={{ width: "100%", height: "100%" }}></img> : null}
<img
className="Sprite"
onLoad={() => this.setState({ imageLoading: false })}
onError={() => this.setState({ tooManyRequests: false })}
src={this.state.thumbnail}
/>
</div>
<div className="card__info">
<span className="card__category">{this.state.title}</span>
<div className="cardButtons">
<Popup trigger={<button className="btn blue-outline">Edit</button>} modal position="left top">
<form
onSubmit={(e) => e.preventDefault()}
id="videoCardEdit"
style={{ width: "auto", height: "auto" }}>
<div>
<div>
<label>Title:</label>
<input
className="input"
style={{ width: "100%" }}
name="videoCardTitle"
onChange={(e) => {
this.setState({ title: e.target.value });
}}
value={this.state.title}></input>
</div>
<div>
<label>URL:</label>
<input
className="input"
style={{ width: "100%" }}
name="videoCardURL"
onChange={(e) => {
this.setState({ url: e.target.value });
}}
value={this.state.url}></input>
</div>
<div>
<label>Thumbnail URL:</label>
<input
className="input"
style={{ width: "100%" }}
name="videoCardThumbnail"
onChange={(e) => {
this.setState({ thumbnail: e.target.value });
}}
value={this.state.thumbnail}></input>
</div>
<br></br>
</div>
<button
className="btn blue-outline"
style={{
float: "left"
}}
onClick={() =>
this.props.editProduct(this.props.id, this.state.title, this.state.url, this.state.thumbnail)
}
id="btn blue-outline">
confirm
</button>
</form>
</Popup>
<button onClick={() => this.props.deleteProduct(this.props.id)} className="btn blue-outline">
Delete
</button>
</div>
</div>
</article>
</div>
</React.Fragment>
);
}
}
I then also have HelpList.tsx that has the following code:
import React, { Component } from "react";
import HelpCard from "./HelpCard";
import "../help/HelpCard.css";
import axios from "axios";
import InfiniteScroll from "react-infinite-scroller";
interface State {
url: string;
title: string;
adminhelpcard: SingleAdminHelpCard[];
error: null;
response: {};
thumbnail: string;
}
interface SingleAdminHelpCard {
id: string;
url: string;
title: string;
thumbnail: string;
}
interface Props {}
export default class HelpList extends Component<Props, State> {
state = {
title: "",
thumbnail: "",
id: "",
url: "http://localhost:3000/videos/",
adminhelpcard: [],
itemsCountPerPage: 1,
activePage: 1,
error: null,
response: {}
};
loadAdminHelpCard = () => {
axios
.get(this.state.url)
.then((res) => {
this.setState((prevState) => {
const adminhelpcard = prevState.adminhelpcard;
return {
adminhelpcard: [...prevState.adminhelpcard, ...res.data],
url: res.data.next
};
});
})
.catch(function(error) {
// handle error
console.log(error);
});
};
static props: any;
async componentDidMount() {
const apiVideoUrl = "http://localhost:3000/videos/";
const apiManualUrl = "http://localhost:3000/manuals/";
const res = await axios.get(this.state.url);
this.setState({ adminhelpcard: res.data });
fetch(apiVideoUrl)
.then((res) => res.json())
.then(
(result) => {
this.setState({
adminhelpcard: result
});
},
(error) => {
this.setState({ error });
}
);
fetch(apiManualUrl)
.then((res) => res.json())
.then(
(result) => {
this.setState({
adminhelpcard: result
});
},
(error) => {
this.setState({ error });
}
);
}
deleteProduct(id: any) {
const { adminhelpcard } = this.state;
const apiVideoUrl = `http://localhost:3000/videos/${id}`;
const apiManualUrl = `http://localhost:3000/manuals/${id}`;
const options = {
method: "DELETE"
};
fetch(apiVideoUrl, options)
.then((res) => res.json())
.then(
(result) => {
this.setState({
response: result,
adminhelpcard: adminhelpcard.filter((adminhelpcard: SingleAdminHelpCard) => adminhelpcard.id !== id)
});
},
(error) => {
this.setState({ error });
}
);
fetch(apiManualUrl, options)
.then((res) => res.json())
.then(
(result) => {
this.setState({
response: result,
adminhelpcard: adminhelpcard.filter((adminhelpcard: SingleAdminHelpCard) => adminhelpcard.id !== id)
});
},
(error) => {
this.setState({ error });
}
);
console.log(this.state.id);
}
editProduct(id: any, title: string, url: string, thumbnail: string) {
const { adminhelpcard } = this.state;
const apiVideoUrl = `http://localhost:3000/videos/${id}`;
const apiManualUrl = `http://localhost:3000/manuals/${id}`;
const options = {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title,
url,
thumbnail
})
};
fetch(apiVideoUrl, options)
.then((res) => res.json())
.then(
(result) => {
this.setState({
response: result,
adminhelpcard: adminhelpcard.filter((adminhelpcard: SingleAdminHelpCard) => adminhelpcard.id !== id)
});
},
(error) => {
this.setState({ error });
}
);
fetch(apiManualUrl, options)
.then((res) => res.json())
.then(
(result) => {
this.setState({
response: result,
adminhelpcard: adminhelpcard.filter((adminhelpcard: SingleAdminHelpCard) => adminhelpcard.id !== id)
});
},
(error) => {
this.setState({ error });
}
);
}
render() {
console.log(this.state.adminhelpcard);
return (
<div>
<React.Fragment>
{this.state.adminhelpcard ? (
<div className="row">
<InfiniteScroll
pageStart={1}
loadMore={this.loadAdminHelpCard}
hasMore={this.state.url ? true : false}
threshold={0}
loader={
<div className="loader" key={0}>
Loading ...
</div>
}>
{this.state.adminhelpcard.map((adminhelpcard: SingleAdminHelpCard, i) => (
<HelpCard
id={adminhelpcard.id}
key={adminhelpcard.id + i}
title={adminhelpcard.title}
url={adminhelpcard.url}
thumbnail={adminhelpcard.thumbnail}
deleteProduct={this.deleteProduct.bind(this)}
editProduct={this.editProduct.bind(this)}
/>
))}
</InfiniteScroll>
</div>
) : (
<h1>Loading Cards</h1>
)}
</React.Fragment>
</div>
);
}
}
So I created these two classes to obtain all the information from localhost:0000/videos before realizing theres also localhost:0000/texts which also send the same data such as "Id, title, URL, thumbnailUrl". How can I go about loading all the cards from both urls simultaneosly? The list now only seems to bring in /texts cards and not both together.
The easiest way to do that is to use a Promise.all(getVideos(),getTexts()). Assuming getVideos() and getTexts() are methods that are returning promises like so:
async function getVideos() {
const res = await fetch('http://localhost:3000/videos/')
return await res.json();
}
async function getText() {
const res = await fetch('http://localhost:3000/texts/')
return await res.json();
}
Promise.all(getVideos(), getTexts()).then(data = {
const [ videos, texts ] = data;
// all data arrived at the same time
})
I would imagine your componentDidMount to look like this:
async componentDidMount() {
const apiVideoUrl = "http://localhost:3000/videos/";
const apiManualUrl = "http://localhost:3000/manuals/";
const getVideos = async() => {
return await axios.get(apiVideoUrl);
}
const getManuals = async() => {
return await axios.get(apiManualUrl);
}
try {
const [videos, manuals] = await Promise.all(getVideos(), getManuals());
// render to state setState({ prop: ? })
} catch(err) {
this.setState({ error });
}
}
not sure why you have two fetch calls in edit and delete, it looks like the calls are overwriting each other?
My goal is to call the function performSearch () by clicking div in the componentItem. The performSerach () function is placed in another select () function. The select () function is passed to the Item component. In console.log returns me active = null.
Items
class Items extends Component {
constructor (props) {
super(props);
this.state = {
items: [],
active: null,
abc: null
}
}
select = (id) => {
this.setState({
abc: id
})
this.performSearch(id);
}
componentDidMount() {
axios.get
axios({
url,
method: "GET",
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(res => {
this.setState({
items: res.data
});
})
.catch(error => {
console.log(error);
})
}
performSearch = (id) => {
axios.get
axios({
url: `https://app.com/api/v1/${id}`,
method: "GET",
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(res => {
this.setState({
active: res.data
});
})
.catch(error => {
console.log(error);
})
}
render () {
<div>
{this.state.items.map((item, index) =>
<Item
select={this.select}
/>
)}
</div>
}
}
Item
class Item extends Component {
render () {
return (
<div onClick={()=> this.props.select(this.props.item.id)}>
</div>
)
}
}
render () {
<div>
{this.state.items.map((item, index) =>
<Item
select={this.select}
/>
)}
</div>
}
Should pass the item the Item component:
render () {
<div>
{this.state.items.map((item, index) =>
<Item
select={this.select}
item={item}
key={index}
/>
)}
</div>
}
Or:
render () {
<div>
{this.state.items.map((item, index) =>
<Item
select={() => this.select(item.id)}
/>
)}
</div>
}
I am experiencing a very weird react behavior. There is this component which gets message and emailSubmitedText. In render method based on some condition it should render either first or the other one.
Now at first it is message. I click on the submit of the form. All the functions happen.
The component rerenders. In the console log I can see this time it should render emailSubmitedText. In react devtools it show the right text.
However in the actual html and html inspector it still shows the previos text.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Details from './Details'
class DefaultMessage extends Component {
inputRef = null
renderEmailForm = () => {
return (
<form
className='b2c_email-form input-field'
onSubmit={e => {
e.preventDefault()
const { projectId, visitSessionId } = this.props
this.setState({ email: this.inputRef.value })
this.props.onSubmitEmail({
email: this.inputRef.value,
convertedPage: window.location.href, projectId, visitSessionId
})
}}
>
<div className="input-field">
<input ref={elem => this.inputRef = elem} id='email' type='email' className='validate' value={this.props.email} />
<label htmlFor='email' data-error='Invalid email address'>E-mail</label>
</div>
<button
className='b2c_email-form-button waves-effect waves-light btn'
type='submit'
style={{
backgroundColor: this.props.companyColor || '#63bc78'
}}
>Submit</button>
</form>
)
}
render = () => {
console.log('...> ', this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText)
return (
<div className='b2c_chat-message'>
<Details
classNames='b2c_chat-message-details__admin'
avatar={this.props.avatar}
name={this.props.name}
date={this.props.date}
/>
<div className='b2c_chat-message-text b2c_chat-message-text__admin b2c_chat-message-default'>
<div className='b2c_chat-message-after b2c_chat-message-after__admin' />
{this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText}
{!this.props.contactId && this.renderEmailForm()}
</div>
</div>
)
}
}
DefaultMessage.propTypes = {
projectId: PropTypes.string.isRequired,
visitSessionId: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
onSubmitEmail: PropTypes.func.isRequired
}
export default DefaultMessage
Here is the direct parent of the component.
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import $ from 'jquery'
import moment from 'moment'
import randomstring from 'randomstring'
import DefaultMessage from './DefaultMessage'
import Message from './Message'
import UserTypingIndicator from '../UserTypingIndicator'
import TypingIndicator from './TypingIndicator'
class Messages extends Component {
chatRef = null
componentDidUpdate () {
this.scrollToTheLastMessage()
}
scrollToTheLastMessage = () => {
const $chat = $(this.chatRef)
const scrollTop = $chat.prop('scrollHeight') - $chat.innerHeight()
$chat.scrollTop(scrollTop)
}
renderDefaultMessage = () => (
<DefaultMessage
contactId={this.props.contactId}
companyColor={this.props.companyColor}
error={this.props.error}
date={moment().format('h:mm A')}
name={this.props.adminName}
avatar={this.props.adminAvatar}
message={this.props.welcomeMessage}
emailSubmitedText={this.props.emailSubmitedText}
projectId={this.props.projectId}
visitSessionId={this.props.visitSessionId}
onSubmitEmail={this.props.onSubmitEmail}
/>
)
renderMessages = () => {
let checkConversationDate = null
const {messages, contactName, adminName, adminAvatar} = this.props
const compareConversationDates = (createdAt) => {
checkConversationDate = moment(createdAt).format("DD.MM.YYYY")
return (
<div key={randomstring.generate()} className='conversationDayDate'>
<span>{checkConversationDate}</span>
</div>
)
}
if (!messages) return null
return messages.map((message, index) => {
return (
<div>
{checkConversationDate !== moment(message.createdAt.$date).format("DD.MM.YYYY") ? compareConversationDates(message.createdAt.$date) : ''}
{/* {index === 0 ? this.renderDefaultMessage() : ''} */}
<Message
isAdmin={message.userId ? true : false}
imageFile={message.imageFile}
key={randomstring.generate()}
companyColor={this.props.companyColor}
contactName={contactName}
adminName={adminName}
avatar={adminAvatar}
message={message.message}
date={moment(message.createdAt.$date).format('h:mm A')}
/>
</div>
)
})
}
renderTypingIndicators = () => {
const arrayToRender = [
this.props.isAdminTyping && <AdminTypingIndicator />,
this.props.isUserTyping && <UserTypingIndicator />
]
return arrayToRender
}
render = () => <div ref={elem => this.chatRef = elem} id='chat' className='chat-body' style={{
height: 'calc(100% - 190px - 3rem)',
overflowY: 'scroll',
margin: '30px 10px 10px 0',
boxSizing: 'border-box'
}}>
{this.renderDefaultMessage()}
{this.renderMessages()}
{this.renderTypingIndicators()}
</div>
}
Messages.propTypes = {
projectId: PropTypes.string.isRequired,
visitSessionId: PropTypes.string.isRequired,
messages: PropTypes.array.isRequired,
adminName: PropTypes.string.isRequired,
contactName: PropTypes.string.isRequired,
onSubmitEmail: PropTypes.func.isRequired
}
export default Messages
And here is where Container with states
import React, { Component } from 'react'
import Sound from 'react-sound'
import ddp from '../../ddp'
import Cookies from 'js-cookie'
import randomstring from 'randomstring'
import ChatContainer from './ChatContainer'
import Icon from './Icon'
import { connect, makeArrayCollectionFromObjectCollection, getNewMessages } from '../../functions'
class View extends Component {
defaultDocumentTitle = null
state = {
contactId: '',
chat: null,
show: false,
newMessagesCount: null,
notStatus: 'STOPPED'
}
newMessageNotification = newMessages => {
if (newMessages.length && newMessages.length > this.state.newMessagesCount) {
this.setState({ notStatus: 'PLAYING' })
document.title = `(${newMessages.length}) ${this.defaultDocumentTitle}`
} else if (!newMessages.length) {
document.title = this.defaultDocumentTitle
}
if (this.state.newMessagesCount !== newMessages.length) {
this.setState({ newMessagesCount: newMessages.length })
}
}
componentWillMount () {
this.defaultDocumentTitle = document.title
}
componentDidMount = async () => {
this.setContactIdFromCookies()
await connect(ddp)
}
setContactIdFromCookies = () => {
window.Cookies = Cookies
console.warn('setContactIdFromCookies')
const contactId = Cookies.get('b2cContactId')
console.log('contactId', contactId)
if (contactId) this.setState({contactId})
}
componentDidUpdate () {
console.warn('componentDidUpdate', this.props)
if (this.state.contactId && !this.state.chat) {
this.getChat(this.state.contactId)
}
if (this.state.chat && this.state.chat.length) {
let newMessages = getNewMessages(this.state.chat)
this.newMessageNotification(newMessages)
}
}
componentWillReceiveProps = (nextProps) => {
console.warn('componentWillReceiveProps', nextProps)
if (!nextProps.contactId) return
if (this.state.chat == null) this.getChat(nextProps.contactId)
}
getChat = async (contactId) => {
console.log('getChat', contactId)
await ddp.subscribe('Messages', {contactId})
const messagesColl = ddp.getCollection('Messages')
console.log('messagesColl', messagesColl)
this.setState({chat: this.getMessages(messagesColl)})
ddp.watch('Messages', (changedDoc, message) => {
console.log('Messages collection item changed', changedDoc, message)
const messagesColl = ddp.getCollection('Messages')
this.setState({chat: this.getMessages(messagesColl)})
})
}
getMessages = collection => {
let messages = []
if (collection) {
messages = makeArrayCollectionFromObjectCollection(collection)
}
console.log('messages', messages)
return messages
}
submitEmail = ({ email, convertedPage, projectId, visitSessionId }) => ddp.call('chat.init', { email, convertedPage, projectId, visitSessionId })
.then(contactId => {
Cookies.set('b2cContactId', contactId, { expires: 90 })
this.setState({ contactId, error: '' })
})
.catch(error => {
console.error('Error >', error)
})
readMessages = () => ddp.call('readMessages', {contactId: this.state.contactId, userId: !null})
.then(res => {
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
})
submitMessage = ({message, visitSessionId, imageFile}) => ddp.call('chat.submitContactMessage', { message, visitSessionId, contactId: this.state.contactId, projectId: this.props.projectId, imageFile: imageFile || null})
.then((res) => {
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
this.setState({error})
})
toggleChat = () => this.setState((state) => ({show: !state.show}))
sendFileToServer = (base64File, resolve, reject) => {
ddp.call('uploadToDropbox', base64File)
.then((res) => {
this.submitMessage({message: '', visitSessionId: this.props.visitSessionId, imageFile: res})
console.log('res', res)
})
.catch(error => {
console.error('Error', error)
})
}
getBase64 = (file, resolve, reject) => {
const self = this
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = function () {
self.sendFileToServer(reader.result, resolve, reject)
}
reader.onerror = function (error) {
console.error('FileReader Error: ', error)
}
}
onFileDrop = files => {
let self = this
files.forEach(file => {
return new Promise((resolve, reject) => {
self.getBase64(file, resolve, reject)
})
})
}
render () {
return (
<div>
<ChatContainer
onFileDrop={this.onFileDrop}
contactId={this.state.contactId}
show={this.state.show}
error={this.state.error && <span style={{color: 'red'}}>{this.state.error}</span>}
chatSettings={this.props.chatSettings}
projectId={this.props.projectId}
visitSessionId={this.props.visitSessionId}
defaultAdminUserName='default defaultAdminUserName'
contactName='You'
supportName='Our Support'
messages={this.state.chat}
onSend={this.submitMessage}
onSubmitEmail={this.submitEmail}
toggleChat={this.toggleChat}
readMessages={this.readMessages}
/>
<Icon
companyColor={this.props.chatSettings.companyColor}
onClick={this.toggleChat}
newMessagesCount={this.state.newMessagesCount}
/>
<Sound
url='https://www.incredo.co/hubfs/b2c/Not%201.wav'
playStatus={this.state.notStatus}
playFromPosition={0}
onFinishedPlaying={() => this.setState({ notStatus: 'STOPPED' })}
/>
</div>
)
}
}
export default View