I want to show a successful or failed alert after the update with fetch. I can show a message with the button, but no alert is shown in the fetch operation.
How to display alert in Fetch
show in react-alert fetch
I'm using the react-alert component.
https://www.npmjs.com/package/react-alert
async handleSubmitUpdate() {
await fetch(`${this.domain}/api/debt/update/`, {
method: "PUT",
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
user: this.props..id,
})
})
.then(response => {
return response.json();
})
.then(json => {
this.componentDidMount();
return (<AlertMessageBox type={"success"} data={"Update succesfully"} />)
})
.catch(err => console.log(err));
}
AlertMessageBox.js
import React from 'react';
import { useAlert } from 'react-alert';
const AlertMessageBox = ({ type, data }) => {
const alert = useAlert();
console.log(data);
const showAlert = () => {
switch (type) {
case 'error':
alert.error(<div style={{color: 'red'}}> data </div>);
return;
case 'show':
alert.show(<div style={{color: 'white'}}> data </div>);
return;
case 'info':
alert.success(<div style={{color: 'green'}}> data </div>);
return;
default:
return;
}
};
return <div> { showAlert() } Deneme</div>;
};
export default AlertMessageBox;
You should have a state variable for this.
state = {
showSuccessAlert: false,
showFailAlert: false
}
Then you can set state variable on success / failure,
await fetch(`${this.domain}/api/debt/update/`, {
method: "PUT",
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
user: this.props..id,
})
})
.then(response => {
return response.json();
})
.then(json => {
//this.componentDidMount(); //I don't think you can call this
//Change the state here which will show your Alert
this.setState({
showSuccessAlert: true
})
})
.catch(err => {
console.log(err);
//Change the state here which will show your Alert
this.setState({
showFailAlert: true
})
);
In your render method you should have your alert with condition,
render(){
return(
<div>
{ this.state.showSuccessAlert && <AlertMessageBox type={"success"} data={"Update succesfully"} /> }
{ this.state.showFailAlert && <AlertMessageBox type={"error"} data={"Data update failed"} /> }
....
</div>
)
}
Related
This is my function in my util module Spotify:
savePlaylist1(name, trackUris) {
if (!name || !trackUris.length) {
return;
}
const accessToken = Spotify.getAccessToken();
const headers = { Authorization: `Bearer ${accessToken}` };
let userID;
return fetch('https://api.spotify.com/v1/me', { headers: headers }
).then(response => {
return response.json();
}).then(jsonResponse => {
userID = jsonResponse.id;
return fetch(`https://api.spotify.com/v1/users/${userID}/playlists`, {
headers: headers,
method: 'POST',
body: JSON.stringify({ name: name })
}).then(response => {
return response.json();
}).then(jsonResponse => {
const playlistID = jsonResponse.id;
return fetch(`https://api.spotify.com/v1/playlists/${playlistID}/tracks`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({ uris: trackUris })
})
})
})
} // end of savePlaylist1 method
}
This is my container module where I'm calling the function from: (followed by the render function)
savePlaylist() {
const trackUris = this.state.playlistTracks.map(track => track.uri);
Spotify.savePlaylist1(this.state.playlistName, trackUris).then(() => {
this.setState({
playlistName: 'New Playlist',
playlistTracks: []
});
})
}
render() {
return (
<div>
<div className="App" >
< SearchBar onSearch={this.search} />
<div className="App-playlist">
< SearchResults searchResults={this.state.searchResults}
onAdd={this.addTrack} />
< Playlist playlistName={this.state.playlistName}
playlistTracks={this.state.playlistTracks}
onRemove={this.removeTrack}
onNameChange={this.updatePlaylistName}
onSave={this.savePlaylist} />
</div>
</div>
</div>);
}
this is the playlist component with the safe button>
render() {
return (
<div className="Playlist">
<input defaultValue={"New Playlist"} onChange={this.handleNameChange} />
<TrackList
tracks={this.props.playlistTracks}
onRemove={this.props.onRemove}
isRemoval={true}
/>
<button className="Playlist-save" onClick={this.props.onSave}>
SAVE TO SPOTIFY
</button>
</div>
);
}
it is saving with an empty list and I get the following error:
{
"error" : {
"status" : 400,
"message" : "Invalid track uri: null"
}
}
Finally, this is the jamming project in codecademy, the problem was that it was not loading the URIs, when being called from the API, this was because it was not included in the search function as part of the list:
search(term) {
const accessToken = Spotify.getAccessToken();
return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
}).then(response => {
return response.json();
}).then(jsonResponse => {
if (!jsonResponse.tracks) {
return [];
}
return jsonResponse.tracks.items.map(track => ({
id: track.id,
name: track.name,
artist: track.artists[0].name,
album: track.album.name,
uri: track.uri //this was missing!!!!
}));
});
},
i'm Trying to make react paypal button that changes the billing amount on props change.
I call the following component with props price and everytime the price change i would like to rerender the button to update the actual price.
const PaypalForm = props => {
let paypalRef = useRef();
useEffect(() => {
window.paypal
.Buttons({
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [
{
description: "test",
amount: {
currency_code: "USD",
value: props.price
}
}
]
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
console.log(order);
},
onError: err => {
setError(err);
console.error(err);
}
})
.render(paypalRef.current);
}, [props.price]);
return (
<Row className="justify-content-center">
{error && <div>Uh oh, an error occurred! {error.message}</div>}
<div ref={paypalRef} />
</Row>
);
};
Everything is working except that a new button is created and added in the bottom of old one at each props change. I would like my new button to replace the old one.
You can pass the amount to the forceRerender property of the button and the button will rerender each whenever the amount is updated.
You should really just use react-paypal-button-v2
It updates with props, works as a stateless function and works with SSR such as next.js.
It even allows bypassing actions.order.create() so that you can call your own API's.
import { PayPalButton } from "react-paypal-button-v2";
const PaypalButton = ({total, cart}) => {
return (
<PayPalButton
createOrder={(data, actions) => {
return fetch('/api/paypal/create-transaction', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
total: total,
cart: cart,
})
})
.then((response) => {return response.json()})
.then((data) => {return data.orderID})
.catch(error => console.log(error))
}}
onApprove={(data) => {
// Capture the funds from the transaction
return fetch('/api/paypal/capture-transaction', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({ orderID: data.orderID })
})
.then((res) => { return res.json() })
.then((details) => {
if(details === 200){
console.log('success');
} else {
console.log('failure');
}
})
.catch(error => {
console.log(error)
})
}}
options={{
clientId: process.env.PAYPAL_CLIENT_ID
}}
/>
);
}
export default PaypalButton;
function getData(access_token) {
fetch('https://api.fitbit.com/1/user/-/activities/steps/date/today/1m.json', {
method: 'GET',
headers: {
Authorization: `Bearer ${access_token}`,
},
// body: `root=auto&path=${Math.random()}`
})
.then(res => res.json())
.then(res => {
console.log(`res: ${JSON.stringify(res)}`);
})
.catch(err => {
console.error('Error: ', err);
});
}
export default class App extends Component {
connectFitbit = () => {
OAuth(client_id, getData);
};
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to Fitbit Integration</Text>
<Button title="connect fitbit" onPress={() => this.connectFitbit()} />
</View>
);
}
}
I'm trying to get fit bit steps JSON data in react native app ,
right now I am getting the output in console ,now i want to parse the step data in my app please help me out
If you want to display your data in to flatlist...
First you need to make array for saving your response data..
constructor(props) {
super(props);
this.state = {
...
result: [],
...
};
}
function getData(access_token) {
fetch('https://api.fitbit.com/1/user/-/activities/steps/date/today/1m.json', {
method: 'GET',
headers: {
Authorization: `Bearer ${access_token}`,
},
})
.then(res => res.json())
.then(res => {
console.log(`res: ${JSON.stringify(res)}`);
// if your response have anu json array
this.setState({
result:res.data,
});
})
.catch(err => {
console.error('Error: ', err);
});
}
After, in flatlist simply set this array,
<FlatList
data={this.state.result}
renderItem={item => your render view}
keyExtractor={(item, index) => your key item}/>
I am making an api call on componentDidMount the problem is that the api takes some seconds to responde in the meanwhile the user could choose to go to another page
doing so another request is made, if this happens the app crashes. How do fix this issue? for the fix I just need to render the pagination component only when all the array is rendered how do I do so?
import React, { Component, Fragment } from "react";
import Episode from "../components/Episode";
import "react-virtualized/styles.css"; // only needs to be imported once
import { withStyles } from "#material-ui/core/styles";
import Typography from "#material-ui/core/Typography";
import Grid from "#material-ui/core/Grid";
import Paper from "#material-ui/core/Paper";
import ButtonBase from "#material-ui/core/ButtonBase";
import CircularProgress from "#material-ui/core/CircularProgress";
import Like from "#material-ui/icons/ThumbUp";
import IconButton from "#material-ui/core/IconButton";
import NextButton from "#material-ui/icons/NavigateNext";
import PreviousButton from "#material-ui/icons/NavigateBefore";
// This example assumes you have a way to know/load this information
const styles = theme => ({
//styles
});
class SeriesPage extends Component {
constructor(props) {
super(props);
this.state = {
apiToken: "",
serie: {
image: "",
description: "",
title: "",
likes: 0,
type: "",
apiName: ""
},
startEpisode: 1,
endEpisode: 10,
episodes: [],
loaded: false,
clicked: false,
enabled: true
};
}
componentDidMount() {
this.initialize(this.state.startEpisode, this.state.endEpisode);
}
initialize = async (startIndex, stopIndex) => {
await this.getTokenFromApi();
await this.getSerieDetailsByApiName();
await this.getEpisodeBySeriesApiNameWithRange(startIndex, stopIndex);
};
getTokenFromApi = async () => {
const data = {
name: "generateToken",
param: {
email: "*",
pass: "*"
}
};
return fetch("*", {
method: "post",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
this.setState({
episodes: "Network request failed"
});
throw Error("Network request failed");
}
return response;
})
.then(res => {
return res.json();
})
.then(content => {
if (content.response.status === 200) {
this.setState({
apiToken: content.response.result.token
});
}
})
.catch(error => {
this.setState({
episodes: "There was an internal error"
});
throw error;
});
};
getSerieDetailsByApiName = async () => {
const data = {
name: "*",
param: {
serieApiName: this.props.match.params.series
}
};
return fetch("*", {
method: "post",
headers: {
Authorization: "Bearer " + this.state.apiToken,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
this.setState({
episodes: "Network request failed"
});
throw Error("Network request failed");
}
return response;
})
.then(response => {
return response.json(); //response.json() is resolving its promise. It waits for the body to load
})
.then(responseData => {
if (responseData.response.status === 200) {
this.setState(
{
serie: responseData.response.result,
loaded: true
},
() => {
console.log(this.state);
}
);
}
})
.catch(error => {
this.setState({
episodes: "There was an internal error"
});
throw error;
});
};
getEpisodeBySeriesApiNameWithRange = async (startIndex, stopIndex) => {
const data = {
name: "*",
param: {
serieApiName: this.props.match.params.series,
startIndex: startIndex,
stopIndex: stopIndex
}
};
return fetch("*", {
method: "post",
headers: {
Authorization: "Bearer " + this.state.apiToken,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
this.setState({
episodes: "Network request failed"
});
throw Error("Network request failed");
}
return response;
})
.then(response => {
return response.json(); //response.json() is resolving its promise. It waits for the body to load
})
.then(responseData => {
if (responseData.response.status === 200) {
this.setState(prevState => ({
episodes: [...prevState.episodes, ...responseData.response.result]
}));
}
})
.catch(error => {
this.setState({
episodes: "There was an internal error"
});
});
};
handleLikeClick = () => {
if (this.state.clicked) {
this.setState(
prevState => ({
clicked: !prevState.clicked,
serie: {
...prevState.serie,
likes: Number(prevState.serie.likes) - 1
}
}),
() => {
const data = {
name: "removeLikeSerie",
param: {
serieApiName: this.state.serie.apiName
}
};
return fetch("*", {
method: "post",
headers: {
Authorization: "Bearer " + this.state.apiToken,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
this.setState({
episodes: "Network request failed"
});
throw Error("Network request failed");
}
return response;
})
.catch(error => {
this.setState({
episodes: "There was an internal error"
});
});
}
);
} else {
this.setState(
prevState => ({
clicked: !prevState.clicked,
serie: {
...prevState.serie,
likes: Number(prevState.serie.likes) + 1
}
}),
() => {
const data = {
name: "likeSerie",
param: {
serieApiName: this.state.serie.apiName
}
};
return fetch("*", {
method: "post",
headers: {
Authorization: "Bearer " + this.state.apiToken,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
this.setState({
episodes: "Network request failed"
});
throw Error("Network request failed");
}
return response;
})
.catch(error => {
this.setState({
episodes: "There was an internal error"
});
});
}
);
}
};
previousPage = () => {
if (this.state.startEpisode === 11) {
this.setState(
prevState => ({
episodes: [],
startEpisode: prevState.startEpisode - 10,
endEpisode: prevState.endEpisode - 10,
enabled: true
}),
() => {
this.initialize(this.state.startEpisode, this.state.endEpisode);
}
);
} else if (this.state.startEpisode > 10) {
this.setState(
prevState => ({
episodes: [],
startEpisode: prevState.startEpisode - 10,
endEpisode: prevState.endEpisode - 10
}),
() => {
this.initialize(this.state.startEpisode, this.state.endEpisode);
}
);
}
};
nextPage = () => {
this.setState(
prevState => ({
episodes: [],
startEpisode: prevState.startEpisode + 10,
endEpisode: prevState.endEpisode + 10,
enabled: false
}),
() => {
this.initialize(this.state.startEpisode, this.state.endEpisode);
}
);
};
renderRow = item => {
const { classes, headerIsHidden, ...other } = this.props;
return <Episode key={item.videoId} episode={item} {...other} />;
};
// Render your list
render() {
const { classes } = this.props;
return (
<Fragment>
<div className={classes.serieDetails}>
{this.state.loaded ? (
<Paper className={classes.root}>
<Grid container spacing={16}>
<Grid item>
<ButtonBase className={classes.image}>
<img
className={classes.img}
alt={this.state.serie.title + " Image"}
src={this.state.serie.image}
/>
</ButtonBase>
</Grid>
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={16}>
<Grid item xs>
<Typography gutterBottom variant="subtitle1">
{this.state.serie.title}
</Typography>
<Typography gutterBottom>
{this.state.serie.description}
</Typography>
<Typography color="textSecondary">
<IconButton
className={classes.button}
className={this.state.clicked ? classes.liked : ""}
aria-label="Like this serie"
onClick={this.handleLikeClick}
>
<Like />
</IconButton>
{this.state.serie.likes}
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Paper>
) : (
""
)}
</div>
<div className={classes.content}>
<div className={classes.innerContent}>
{this.state.episodes.constructor === String ? (
this.state.episodes
) : (
<div>
{this.state.episodes.map(this.renderRow)}
<div className={classes.pagination}>
<IconButton
aria-label="Previous"
className={classes.button}
onClick={this.previousPage}
disabled={this.state.enabled}
>
<PreviousButton />
</IconButton>
<IconButton
aria-label="Next"
className={classes.button}
onClick={this.nextPage}
>
<NextButton />
</IconButton>
</div>
</div>
)}
</div>
</div>
</Fragment>
);
}
}
export default withStyles(styles, { withTheme: true })(SeriesPage);
The pagination component is the div with className={classes.pagination}
the array is stored in the state
Add to your state a variable called loading which is initially true:
state = {
...,
loading: true
}
After the response return of getEpisodeBySeriesApiNameWithRange you can setState of loading to be false:
getEpisodeBySeriesApiNameWithRange = async (startIndex, stopIndex) => {
const data = {
name: "*",
param: {
serieApiName: this.props.match.params.series,
startIndex: startIndex,
stopIndex: stopIndex
}
};
return fetch("*", {
method: "post",
headers: {
Authorization: "Bearer " + this.state.apiToken,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
this.setState({
episodes: "Network request failed",
loading: false
});
throw Error("Network request failed");
}
return response;
})
.then(response => {
return response.json(); //response.json() is resolving its promise. It waits for the body to load
})
.then(responseData => {
if (responseData.response.status === 200) {
this.setState(prevState => ({
episodes: [...prevState.episodes, ...responseData.response.result],
loading: false
}));
}
})
.catch(error => {
this.setState({
episodes: "There was an internal error",
loading: false
});
});
};
When you click on nextPage or previousPage setState of loading to be true again:
nextPage = () => {
this.setState(
prevState => ({
episodes: [],
startEpisode: prevState.startEpisode + 10,
endEpisode: prevState.endEpisode + 10,
enabled: false,
loading: true
}),
() => {
this.initialize(this.state.startEpisode, this.state.endEpisode);
}
);
};
the previousPage will be the same.
In render you will only render the component if the loading is false which mean the data is fetched:
{this.state.loading ? null : <div className={classes.content}>
<div className={classes.innerContent}>
{this.state.episodes.constructor === String ? (
this.state.episodes
) : (
<div>
{this.state.episodes.map(this.renderRow)}
<div className={classes.pagination}>
<IconButton
aria-label="Previous"
className={classes.button}
onClick={this.previousPage}
disabled={this.state.enabled}
>
<PreviousButton />
</IconButton>
<IconButton
aria-label="Next"
className={classes.button}
onClick={this.nextPage}
>
<NextButton />
</IconButton>
</div>
</div>
)}
</div>
</div>
}
I'm trying to add toast notifications to my app, one plugin I've been trying to use is react-toastify.
The issue I'm having is probably more a general react/redux issue more than with a plugin such as react-toastify.
I'm using a reducer to set the redux state for errors and success messages, from what I understand with the current code, each error or success message is persistent in the store until another action is called to clear them.
The issue I can't figure out is how do I trigger a toast only once. Eg. I enter the wrong credentials, it creates an error toast, but whenever the state changes and reloads (typing anything into the email or password fields) it creates another toast.
How do I get it to only show once?
userActions.js
function handleErrors(res) {
if (res.ok) {
return res.json();
} else {
return res.json().then(err => {throw err;});
}
}
export const login = (user) => dispatch => {
fetch(`${url}/login`,
{
credentials: 'include',
method: 'post',
body: user,
headers: new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
})
.then(handleErrors)
.then(res =>
dispatch({
type: LOGIN,
payload: res
})
)
.catch(error =>
dispatch({
type: ERROR,
payload: error
})
)
}
userReducer.js
const initialState = {
errors: '',
success: ''
};
export default function(state = initialState, action) {
switch (action.type) {
case LOGIN:
return {
...state,
errors: '',
success: action.payload.message
};
case ERROR:
return {
...state,
success: '',
errors: action.payload.message
}
default:
return state;
}
}
app.js
app.post('/login', function(req, res) {
... return res.status(500).send({ message: 'Wrong credentials' });
... return res.status(200).send({ message: 'good!' });
});
login.js
class Login extends React.Component {
constructor() {
super();
this.state = {
email: "",
password: ""
}
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
render() {
const { errors, login, success } = this.props;
if (success !== '') toast.success(success, {
position: toast.POSITION.TOP_CENTER
});
if (errors !== '') toast.error(errors, {
position: toast.POSITION.TOP_CENTER
});
return (
<div>
<input type="text" id="email" placeholder="Email Address" onChange={this.handleChange} />
<input type="password" id="password" placeholder="Password" onChange={this.handleChange} />
<button onClick={() => login(JSON.stringify({email: this.state.email, password: this.state.password}))}>Log In</button>
<ToastContainer />
</div>
)
}
}
const mapStateToProps = state => ({
errors: state.store.errors,
success: state.store.success
});
export default connect(mapStateToProps, {login})(Login);
You're calling toast.success or toast.error inside render which makes a new toast pop up every time you re-render the component.
The solution is simple. Move your toast calls outside render, where they will only be called once.
One way to achieve this is to return a value from your userAction.
export const login = (user) => dispatch => {
return new Promise((resolve, reject) => {
fetch(`${url}/login`,
{
credentials: 'include',
method: 'post',
body: user,
headers: new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
})
.then(handleErrors)
.then(res => {
dispatch({
type: LOGIN,
payload: res
})
resolve(res)
}
)
.catch(error => {
dispatch({
type: ERROR,
payload: error
})
reject(error)
}
)
}
}
Then use that value to toast in login.js.
class Login ... {
...
loginUser = () => {
this.props.login(JSON.stringify({email: this.state.email, password: this.state.password}))
.then(res => {
toast.success(res.message, { position: toast.POSITION.TOP_CENTER })
}
).catch(error => {
toast.error(error.message, { position: toast.POSITION.TOP_CENTER })
}
)
}
...
render() {
return (
...
<button onClick={this.loginUser}>Log In</button>
...
)
}
}
There are other ways to achieve the same functionality and depending on the structure of your project, you may want to toast in a more generalized way.