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>
}
Related
As a beginner to the React Js I'm trying simple projects for hands-on experience and to learn as well! so, in the same way I've created one login portal with react js and WAMP server by watching YouTube tutorial
in the middle I went through some errors but somehow I managed and get them off but after completing the project when I'm trying to login with the credentials which I've given in database the response and all was clear but the page doesn't redirect to welcome page after clicking on login button instead it redirects to welcome page after refreshing manually. I couldn't identify the mistake in my code.
LoginForm.js
import React from 'react';
import InputField from './InputField';
import SubmitButton from './SubmitButton';
import UserStore from './stores/UserStore';
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
username: "",
password: "",
buttonDisabled: false
}
}
setInputValue(property, val) {
val = val.trim();
if (val.length > 12) {
return;
}
this.setState({
[property]: val
})
}
resetForm() {
this.setState({
username: "",
password: "",
buttonDisabled: false
})
}
async doLogin() {
if (!this.state.username) {
return;
}
if (!this.state.password) {
return;
}
this.setState({
buttonDisabled: true
})
try {
let res = await fetch('/login', {
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password
})
});
let result = await res.json();
if (result && result.succes) {
UserStore.isLoggedIn = true;
UserStore.username = result.username;
}
else if (result && result.succes === false) {
this.resetForm();
alert(result.msg);
}
} catch (e) {
console.log(e);
this.resetForm();
}
}
render() {
return (
<div className="loginForm">
Log In
<InputField
type='text'
placeholder='Username'
value={this.state.username ? this.state.username : ''}
onChange={(val) => this.setInputValue('username', val)}
/>
<InputField
type='password'
placeholder='Password'
value={this.state.password ? this.state.password : ''}
onChange={(val) => this.setInputValue('password', val)}
/>
<SubmitButton
text='Log In'
disabled={this.state.buttonDisabled}
onClick={() => this.doLogin()}
/>
</div>
);
}
}
export default LoginForm;
SubmitButton.js
import React from 'react';
class SubmitButton extends React.Component {
render() {
return (
<div className="submitButton">
<button
className='btn'
type='submit'
disabled={this.props.disabled}
onClick={() => this.props.onClick()}
>
{this.props.text}
</button>
</div>
);
}
}
export default SubmitButton;
App.js
import React from 'react';
import { observer } from 'mobx-react';
import UserStore from './stores/UserStore';
import LoginForm from './LoginForm';
import SubmitButton from './SubmitButton';
import './App.css';
class App extends React.Component {
async componentDidMount() {
try {
let res = await fetch('/isLoggedIn', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
let result = await res.json();
if (result && result.success) {
UserStore.loading = false;
UserStore.isLoggedIn = true;
UserStore.username = result.username;
}
else {
UserStore.loading = false;
UserStore.isLoggedIn = false;
}
}
catch (e) {
UserStore.loading = false;
UserStore.isLoggedIn = false;
}
}
async doLogout() {
try {
let res = await fetch('/logout', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-type': 'application/json'
}
});
let result = await res.json();
if (result && result.success) {
UserStore.isLoggedIn = false;
UserStore.username = '';
}
}
catch (e) {
console.log(e);
}
}
render() {
if (UserStore.loading) {
return (
<div className="app">
<div className='container'>
Loading ,Please Wait...
</div>
</div>
);
}
else {
if (UserStore.isLoggedIn) {
return (
<div className="app">
<div className='container'>
Welcome {UserStore.username}
<SubmitButton
text={'Log Out'}
disabled={false}
onClick={() => this.doLogout()}
/>
</div>
</div>
);
}
return (
<div className="app">
<div className='container'>
<LoginForm />
</div>
</div>
);
}
}
}
export default observer(App);
Router.js
const bcrypt = require('bcrypt');
class Router {
constructor(app, db) {
this.login(app, db);
this.logout(app, db);
this.isLoggedIn(app, db);
}
login(app, db) {
app.post('/login', (req, res) => {
let username = req.body.username;
let password = req.body.password;
username = username.toLowerCase();
if (username.length > 12 || password.length > 12) {
res.json({
success: false,
msg: 'An error occured,please try again'
})
return;
}
let cols = [username];
db.query('SELECT * FROM user WHERE username =? LIMIT 1', cols, (err, data, fields) => {
if (err) {
res.json({
success: false,
msg: 'An error occured,please try again'
});
return;
}
if (data && data.length === 1) {
bcrypt.compare(password, data[0].password, (bcryptErr, verified) => {
if (verified) {
req.session.userID = data[0].id;
res.json({
success: true,
username: data[0].username
})
return;
}
else {
res.json({
success: false,
msg: 'Invalid Password'
})
}
});
} else {
res.json({
success: false,
msg: 'User not Found,please try again..!'
})
}
});
});
}
logout(app, db) {
app.post('/logout',(req,res)=>{
if(req.session.userID){
req.session.destroy();
res.json({
success:true
})
return true;
}
else{
res.json({
success:false
})
return false;
}
})
}
isLoggedIn(app, db) {
app.post('/isLoggedIn',(req,res)=>{
if(req.session.userID){
let cols =[req.session.userID];
db.query('SELECT * FROM user WHERE ID=? LIMIT 1',cols,(err,data,fields) =>{
if(data && data.length ===1){
res.json({
success:true,
username:data[0].username
});
return true;
}
else{
res.json({
success:false
});
}
});
}
else{
res.json({
success:false
})
}
});
}
}
module.exports = Router;
you have to change some logic in LoginForm.js. There are multiple ways in which you can do that. I am showing you one example.
import React from 'react';
import InputField from './InputField';
import SubmitButton from './SubmitButton';
import UserStore from './stores/UserStore';
import {Redirect} from 'react-router-dom' //add a redirect from react-router-dom
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
username: "",
password: "",
buttonDisabled: false,
redirectToRefer : false //add a state here for redirect
}
}
setInputValue(property, val) {
val = val.trim();
if (val.length > 12) {
return;
}
this.setState({
[property]: val
})
}
resetForm() {
this.setState({
username: "",
password: "",
buttonDisabled: false
})
}
async doLogin() {
if (!this.state.username) {
return;
}
if (!this.state.password) {
return;
}
this.setState({
buttonDisabled: true
})
try {
let res = await fetch('/login', {
method: "post",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: this.state.username,
password: this.state.password
})
});
let result = await res.json();
if (result && result.succes) {
UserStore.isLoggedIn = true;
UserStore.username = result.username;
this.setState({redirectToRefer:true})//set rediectToRefer as true
}
else if (result && result.succes === false) {
this.resetForm();
alert(result.msg);
}
} catch (e) {
console.log(e);
this.resetForm();
}
}
render() {
//add a condition at render if redirectToRefer is true then redirect to your page
if(redirectToRefer)
{
return <Redirect to ="/"/>
}
return (
<div className="loginForm">
Log In
<InputField
type='text'
placeholder='Username'
value={this.state.username ? this.state.username : ''}
onChange={(val) => this.setInputValue('username', val)}
/>
<InputField
type='password'
placeholder='Password'
value={this.state.password ? this.state.password : ''}
onChange={(val) => this.setInputValue('password', val)}
/>
<SubmitButton
text='Log In'
disabled={this.state.buttonDisabled}
onClick={() => this.doLogin()}
/>
</div>
);
}
}
export default LoginForm;
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 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>
)
}
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;
I am using React native 0.60.4 with react-native-router-flux 4.0.6.
I have a login component which works fine but when I redirect the user to login screen after the user has changed the password, the above error pops up and the app crashes.
I tried printing the state variable in ComponentDidMount() - it gets printed. All the logs in the constructor where I am initializing the state variable are printed.
Post this the error pops up and the app crashes.
Part of error message:
[08:42:09] I | ReactNativeJS ▶︎ login componentDidMount
[08:42:09] E | ReactNativeJS ▶︎ ReferenceError: Can't find variable: state
I believe there is no issue in the code since its working fine whenever the app is loaded.
CODE:
import * as React from 'react';
// import React, { Component } from "react";
import {
StyleSheet,
Text,
TouchableOpacity,
ToastAndroid,
AppState,
Dimensions,
View,
ImageBackground,
Image,
DeviceEventEmitter,
LayoutAnimation,
UIManager,
Platform,
Keyboard
// AsyncStorage
// NetInfo
} from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
import NetInfo from '#react-native-community/netinfo';
import { Actions } from 'react-native-router-flux';
import { Bars } from 'react-native-loader';
import Nointernet from './NoInternet';
import axios from 'axios';
import TextField from './lib/TextField';
import { PermissionsAndroid } from 'react-native';
import firebase from 'react-native-firebase';
let deviceWidth = Dimensions.get('window').width;
let elementWidth = 0.8*deviceWidth;
let screenHeigth = Dimensions.get('window').height;
const unsubscribe=NetInfo.addEventListener((change)=>{ this.handleConnectivityChange });
class LoginScreen extends React.Component {
constructor(props) {
console.log("Props done");
super(props);
console.log("State issue");
this.state = {
appState: AppState.currentState,
hidePwd:true,
pntoken: '',
username: '',
passwd: '',
text: '',
checkinternet:false,
nointernet:false,
isLoading: false ,
visibleHeight: Dimensions.get('window').height ,
logo_height:100,
logo_width:100,
pwd_backup:''
};
console.log("State initialized");
this.handleConnectivityChange = this.handleConnectivityChange.bind(this);
this._handleAppStateChange = this._handleAppStateChange.bind(this);
this.requestLocationPermission = this.requestLocationPermission.bind(this);
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
async getfcmtooken(){
let pn_token = await firebase.messaging().getToken();
if(pn_token){
console.log("PNTOKEN")
// if(this.state!=undefined)
// this.setState({ pntoken: pn_token});
console.log("have TOKN");
// if(this.state!=undefined)
// console.log(this.state.pntoken);
this.updateToken(pn_token);
}
else{
console.log("Loggged in. PN_TOKEN")
this.tokenRefreshListener = firebase.messaging().onTokenRefresh((pn_token) => {
console.log('response getfcmtooken');
if (pn_token) {
console.log("got token")
// if(this.state!=undefined)
// this.setState({ pntoken: pn_token})
this.updateToken(pn_token);
// if(this.state!=undefined)
// console.log(this.state.pntoken);
}
});
console.log('getfcmtooken')
}
}
updateToken(token) {
console.log('App updateToken');
AsyncStorage.getItem('org_id').then((orgId) => {
if(orgId != null){
console.log('orgId');
console.log(orgId);
let org_id = JSON.parse(orgId);
console.log('org_id');
console.log(org_id);
AsyncStorage.getItem('email').then((useremail) => {
let email = JSON.parse(useremail);
if(email){
console.log('email');
AsyncStorage.getItem('token').then((tokenvalue) => {
if(tokenvalue!=null){
console.log('user_id');
console.log(tokenvalue);
let tokenV = JSON.parse(tokenvalue);
console.log("PN TOKEN");
console.log(token);
axios({
method: 'PUT',
url: this.props.apiurl+'/pushnotification/updatetoken',
data: JSON.stringify({
email: email,
pntoken: token,
}),
timeout: 30000,
headers:{
Accept: 'application/json',
'Content-Type': 'application/json',
authorization:tokenV
}
}).then((response)=>{
console.log("RESPONSE")
console.log(response);
})
.catch(error=>{
console.log("ERROR")
console.log(error);
})
// return fetch(this.props.apiurl+'/pushnotification/updatetoken', {
// method: 'PUT',
// headers: {
// Accept: 'application/json',
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify({
// email: email,
// pntoken: token,
// }),
// })
// .then((response) => {
// console.log('222222222222');
// console.log(response);
// response.json()
// })
// .then((responseJson) => {
// console.log('kkkkkkkkkkkkkkk');
// console.log(responseJson);
// })
// .catch((error) => {
// console.log('error');
// console.log(error);
// });
}
});
}
});
}
});
}
async requestCameraPermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,{
'title': 'Camera Permission',
'message': 'needs access to your camera '
}
)
if (granted) {
console.log("You can use the camera")
this.requestLocationPermission()
}
else {
console.log("Camera permission denied")
}
} catch (err) {
console.warn(err)
}
}
async requestLocationPermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
'title': 'Camera Permission',
'message': 'needs access to your location '
}
)
if (granted) {
console.log("You can use the location")
}
else {
console.log("location permission denied")
}
}
catch (err) {
console.warn(err)
}
}
componentDidMount() {
console.log(this.state);
console.log("login componentDidMount");
this.getfcmtooken();
NetInfo.fetch().then((connectionInfo) => {
if(connectionInfo.type != 'none'){
console.log("NETINFO");
this.setState({ checkinternet : false ,nointernet : false });
this.requestCameraPermission();
}
else{
this.setState({ checkinternet : false ,nointernet : true });
}
})
.catch((response)=>{
console.log(response);
});
AppState.addEventListener('change', this._handleAppStateChange);
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow.bind(this))
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide.bind(this))
}
_handleAppStateChange = (nextAppState) => {
console.log("CHECKING STATE");
if (this.state.appState.match(/active/) && nextAppState === 'background') {
console.log('App has come to the background!')
}
console.log("CHECKED STATE")
this.setState({appState: nextAppState});
console.log("NEW STATE SET");
NetInfo.fetch()
.then((connectionInfo) => {
if(connectionInfo.type != 'none'){
this.setState({ nointernet : false });
}
else{
this.setState({ nointernet : true });
}
})
.catch((response)=>{
console.log(response);
});
}
handleConnectivityChange() {
NetInfo.fetch().then((connectionInfo) => {
if(connectionInfo.type != 'none'){
console.log("INTERNET CHECK 1")
this.setState({ nointernet : false });
console.log("INTERNET CHECKED 1")
}
else{
console.log("INTERNET CHECK 2")
this.setState({ nointernet : true });
console.log("INTERNET CHECKED 2")
}
})
.catch((response)=>{
console.log(response);
});
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
NetInfo.removeEventListener('connectionChange', this.handleConnectivityChange );
// unsubscribe();
this.keyboardDidShowListener.remove()
this.keyboardDidHideListener.remove()
}
keyboardDidShow (e) {
console.log('Key Event');
console.log(Dimensions.get('window').height+' - '+e.endCoordinates.height);
let newSize = Dimensions.get('window').height - e.endCoordinates.height
console.log('new size')
console.log(newSize);
console.log('wind_width')
console.log(Dimensions.get('window').width);
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
this.setState({
visibleHeight: newSize,
logo_width:50,
logo_height:50
})
}
keyboardDidHide (e) {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
this.setState({
visibleHeight: Dimensions.get('window').height,
logo_height:100,
logo_width:100
})
}
managePwd(){
this.setState({
hidePwd:!this.state.hidePwd
})
}
login(){
if(this.state.username != ''){
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(reg.test(this.state.username) === true){
if(this.state.passwd != ''){
return fetch(this.props.apiurl+'/mobilelogin', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: this.state.username,
password: this.state.passwd,
pntoken: this.state.pntoken,
}),
})
.then((response) => response.json())
.then((responseJson) => {
if(responseJson.statusCode == 200){
this.setState({
isLoading: false,
text: responseJson.result.authenticateUser.userRole,
dataSource: responseJson
}, function() {
try{
console.log('TOKEN');
console.log(JSON.stringify(responseJson.result.authenticateUser.token));
AsyncStorage.setItem('token', JSON.stringify(responseJson.result.authenticateUser.token));
AsyncStorage.setItem('email', JSON.stringify(responseJson.result.authenticateUser.email));
AsyncStorage.setItem('userRole', JSON.stringify(responseJson.result.authenticateUser.userRole));
AsyncStorage.setItem('org_id', JSON.stringify(responseJson.result.authenticateUser.orgId));
AsyncStorage.setItem('user_id', JSON.stringify(responseJson.result.authenticateUser.userId));
this.getfcmtooken();
AppState.removeEventListener('change', this._handleAppStateChange);
unsubscribe();
// NetInfo.removeEventListener('connectionChange', this.handleConnectivityChange );
Actions.landing();
} catch (error) {
console.log(error)
}
});
}
else{
ToastAndroid.show(responseJson.authenticateUser.statusMessage,ToastAndroid.LONG);
}
})
.catch((error) => {
console.log('error');
console.log(error);
});
}
else{
ToastAndroid.show('Password is mandatory',ToastAndroid.LONG);
}
}
else{
ToastAndroid.show('Email is not in correct form',ToastAndroid.LONG);
}
}
else{
ToastAndroid.show('Email is mandatory',ToastAndroid.LONG);
}
}
render(){
console.log("RENDERING ...");
if(this.state.checkinternet){
console.log("LOADING...CONNECTION ")
return <View style={{flex:1, backgroundColor: '#fff'}}>
<View style={{height:screenHeigth-60, justifyContent:'center',alignItems:'center'}}>
<Bars size={25} color="#eb5828" />
</View>
</View>;
}
if (this.state.nointernet) {
return <View>
<Nointernet/>
</View>;
}
return (
<View style={[styles.container,{height: this.state.visibleHeight}]}>
<ImageBackground
style={[styles.topLogo1,{height: this.state.visibleHeight}]}
source={require('../assets/Background.png')}
>
<View style={styles.upperContainer}>
<View style={styles.signintextContainer}>
<Image style={{height:this.state.logo_height,width:this.state.logo_width}} source={require('../assets/WhiteLogo.png')}/>
<Text style={styles.appname}>Smart Timer</Text>
</View>
</View>
<View style={styles.formContainer}>
<View style={[{width:elementWidth},styles.elementcontainer]}>
<TextField
label={'E-mail ID'}
highlightColor={'#fff'}
onChangeText={(username) => this.setState({username})}
value={this.state.username}
keyboardType='email-address'
autoCapitalize='none'
textColor='#fff'
wrapperStyle={{ height: 60 }}
labelStyle={{ color: '#fff', fontFamily: 'Lato-Regular' }}
/>
</View>
<View style={[{width:elementWidth},styles.elementcontainer]}>
<View style={styles.textBoxBtnHolder}>
<TextField
label={'Password'}
highlightColor={'#fff'}
secureTextEntry={this.state.hidePwd}
onChangeText={(passwd) => this.setState({passwd})}
value={this.state.passwd}
autoCapitalize='none'
textColor='#fff'
wrapperStyle={{ height: 60 }}
labelStyle={{ color: '#fff', fontFamily: 'Lato-Regular' }}
/>
<TouchableOpacity style={styles.visibilityBtn} onPress={this.managePwd.bind(this)} >
<Image style={{ marginRight:10, height: 25,width: 25 }} source={this.state.hidePwd?require('../assets/private.png'):require('../assets/view.png')}/>
</TouchableOpacity>
</View>
</View>
<TouchableOpacity style={[ {width:elementWidth},styles.buttonRaised ]} onPress={this.login.bind(this)} >
<Text style={[ styles.buttonText ]}>Login</Text>
</TouchableOpacity>
<View style={styles.signupTextContainer}>
<View>
<TouchableOpacity onPress={() => Actions.forgotpassword({apiurl:this.props.apiurl})} >
<Text style={[ styles.signupText ]}>Forgot Password | </Text>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity onPress={() => Actions.register({apiurl:this.props.apiurl})} >
<Text style={[ styles.signupTextblack ]}> Sign Up</Text>
</TouchableOpacity>
</View>
</View>
</View>
</ImageBackground>
</View>
);
}
}
I had this code running fine before I upgraded from react-native 0.56.0 to 0.60.4 and also I upgraded react-native-router-flux from ^4.0.0-beta.28 to ^4.0.6. Not very sure if this piece of information helps.
This is because of your unsubscribe function being defined outside of class, which is getting called in login function, moreover when you are calling unsubscribe in your login it should simply be NetInfo.removeEventListener(...)
and
NetInfo.addEventListener((change) => { this.handleConnectivityChange });
should be change to
this.unsubscribe = NetInfo.addEventListener('connectionChange', this.handleConnectivityChange);
So finally it should be something like:
let deviceWidth = Dimensions.get('window').width;
let elementWidth = 0.8 * deviceWidth;
let screenHeigth = Dimensions.get('window').height;
class LoginScreen extends React.Component {
constructor(props) {
console.log("Props done");
super(props);
console.log("State issue");
this.state = {
appState: AppState.currentState,
hidePwd: true,
pntoken: '',
username: '',
passwd: '',
text: '',
checkinternet: false,
nointernet: false,
isLoading: false,
visibleHeight: Dimensions.get('window').height,
logo_height: 100,
logo_width: 100,
pwd_backup: ''
};
console.log("State initialized");
this.handleConnectivityChange = this.handleConnectivityChange.bind(this);
this.unsubscribe = NetInfo.addEventListener('connectionChange', this.handleConnectivityChange);
this._handleAppStateChange = this._handleAppStateChange.bind(this);
this.requestLocationPermission = this.requestLocationPermission.bind(this);
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
componentDidMount() {
console.log(this.state);
console.log("login componentDidMount");
this.getfcmtooken();
NetInfo.fetch().then((connectionInfo) => {
if (connectionInfo.type != 'none') {
console.log("NETINFO");
this.setState({ checkinternet: false, nointernet: false });
this.requestCameraPermission();
}
else {
this.setState({ checkinternet: false, nointernet: true });
}
})
.catch((response) => {
console.log(response);
});
AppState.addEventListener('change', this._handleAppStateChange);
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow.bind(this))
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide.bind(this))
}
login() {
if (this.state.username != '') {
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/;
if (reg.test(this.state.username) === true) {
if (this.state.passwd != '') {
return fetch(this.props.apiurl + '/mobilelogin', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: this.state.username,
password: this.state.passwd,
pntoken: this.state.pntoken,
}),
})
.then((response) => response.json())
.then((responseJson) => {
if (responseJson.statusCode == 200) {
this.setState({
isLoading: false,
text: responseJson.result.authenticateUser.userRole,
dataSource: responseJson
}, function () {
try {
console.log('TOKEN');
console.log(JSON.stringify(responseJson.result.authenticateUser.token));
AsyncStorage.setItem('token', JSON.stringify(responseJson.result.authenticateUser.token));
AsyncStorage.setItem('email', JSON.stringify(responseJson.result.authenticateUser.email));
AsyncStorage.setItem('userRole', JSON.stringify(responseJson.result.authenticateUser.userRole));
AsyncStorage.setItem('org_id', JSON.stringify(responseJson.result.authenticateUser.orgId));
AsyncStorage.setItem('user_id', JSON.stringify(responseJson.result.authenticateUser.userId));
this.getfcmtooken();
AppState.removeEventListener('change', this._handleAppStateChange);
// this.unsubscribe();
NetInfo.removeEventListener('connectionChange', this.handleConnectivityChange );
Actions.landing();
} catch (error) {
console.log(error)
}
});
}
else {
ToastAndroid.show(responseJson.authenticateUser.statusMessage, ToastAndroid.LONG);
}
})
.catch((error) => {
console.log('error');
console.log(error);
});
}
else {
ToastAndroid.show('Password is mandatory', ToastAndroid.LONG);
}
}
else {
ToastAndroid.show('Email is not in correct form', ToastAndroid.LONG);
}
}
else {
ToastAndroid.show('Email is mandatory', ToastAndroid.LONG);
}
}
}
I have only pasted the changed function.
Well, you must have used state directly instead of this.state which is causing this issue. Anything initialised in constructor without an data type should be considered initialised using this operator to make it accessible throughout the class using this.abc.