How to handle response.item is undefined? - reactjs

I am getting the error below although I bound getNowPlaying, can someone help if I am missing something in my code? Is the way I tried to import SpotifyWebApi wrong?
Here is the code that causes problems:
import React, { Component } from 'react';
import './App.css';
import SpotifyWebApi from 'spotify-web-api-js';
const spotifyApi = new SpotifyWebApi();
class App extends Component {
constructor(props){
super(props);
const params = this.getHashParams();
const token = params.access_token;
if (token) {
spotifyApi.setAccessToken(token);
}
this.state = {
loggedIn: token ? true : false,
nowPlaying: { name: 'Not Checked', albumArt: '' }
}
this.getNowPlaying=this.getNowPlaying.bind(this);
}
getNowPlaying(){
spotifyApi.getMyCurrentPlaybackState()
.then((response) => {
this.setState({
nowPlaying: {
name: response.item.name,
albumArt: response.item.album.images[0].url
}
});
})
}
render() {
return (
<div className="App">
<a href='http://localhost:8888' > Login to Spotify </a>
<div>
Now Playing: { this.state.nowPlaying.name }
</div>
</div>
);
}
}
export default App;

According to Spotify docs
Response A successful request will return a 200 OK response code with
a json payload that contains information about the currently playing
track or episode and its context (see below). The information returned
is for the last known state, which means an inactive device could be
returned if it was the last one to execute playback.
When no available devices are found, the request will return a 200 OK
response but with no data populated.
When no track is currently playing, the request will return a 204 NO
CONTENT response with no payload.
If private session is enabled the response will be a 204 NO CONTENT
with an empty payload.
That means that even if your request is successful (200 OK), the response can be empty so you need to do defensive programming by checking if the response is not empty
I am not sure what is the exact object the request returns but you can do something like this
spotifyApi.getMyCurrentPlaybackState()
.then((response) => {
this.setState({
nowPlaying: {
name: response && response.item && response.item.name,
albumArt: response && response.item && response.item.album && response.item.album.images.length > 0 && response.item.album.images[0].url
}
});
})
The problem here is that the properties you need are deeply nested, and also you need to check if the images array length is greater than 0, so probably a better approach is to store all the response object in the state and then checking it at render time.
if that's not possible you can use optional chaining but for that you will have to use a babel plugin https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining
And then you can do
spotifyApi.getMyCurrentPlaybackState()
.then((response) => {
this.setState({
nowPlaying: {
name: response?.item?.name,
albumArt: response?.item?.album?.images[0].url
}
});
})

Related

React - PayPal Button fires without checking conditions

I'm using react-paypal-express-checkout
I've to options: Cash and PayPal.
Cash working fine and checks all conditions.
But bcs PayPal is a seperate component in my CartScreen component it opens and don't check a single if conditions and opens the PayPal window
The CashButton comes with function "cashTranSuccess" it's the same function as "TranSuccess"
just without the paymentID bcs it's only needed for react-paypal-express-checkout
So what I'm looking for is, to check all TranSuccess() conditions before open the PayPal window.
PayPalButton.js
import React from 'react';
import PaypalExpressBtn from 'react-paypal-express-checkout';
export default class PayPalButton extends React.Component {
render() {
const onSuccess = (payment) => {
// Congratulation, it came here means everything's fine!
console.log('The payment was succeeded!', payment);
// You can bind the "payment" object's value to your state or props or whatever here, please see below for sample returned data
this.props.tranSuccess(payment);
};
const onCancel = (data) => {
// User pressed "cancel" or close Paypal's popup!
console.log('The payment was cancelled!', data);
// You can bind the "data" object's value to your state or props or whatever here, please see below for sample returned data
};
const onError = (err) => {
// The main Paypal's script cannot be loaded or somethings block the loading of that script!
console.log('Error!', err);
// Because the Paypal's main script is loaded asynchronously from "https://www.paypalobjects.com/api/checkout.js"
// => sometimes it may take about 0.5 second for everything to get set, or for the button to appear
};
let env = 'sandbox'; // you can set here to 'production' for production
let currency = 'EUR'; // or you can set this value from your props or state
let carttotal = this.props.carttotal; // same a s above, this is the total amount (based on currency) to be paid by using Paypal express checkout
// Document on Paypal's currency code: https://developer.paypal.com/docs/classic/api/currency_codes/
const client = {
sandbox:
'',
production: 'YOUR-PRODUCTION-APP-ID',
};
// In order to get production's app-ID, you will have to send your app to Paypal for approval first
// For sandbox app-ID (after logging into your developer account, please locate the "REST API apps" section, click "Create App"):
// => https://developer.paypal.com/docs/classic/lifecycle/sb_credentials/
// For production app-ID:
// => https://developer.paypal.com/docs/classic/lifecycle/goingLive/
// NB. You can also have many Paypal express checkout buttons on page, just pass in the correct amount and they will work!
// Style Options: https://developer.paypal.com/docs/checkout/standard/customize/buttons-style-guide/ ; https://wise.com/gb/blog/custom-paypal-button
let style = {
size: 'medium',
color: 'gold',
label: 'pay',
tagline: false,
};
return (
<PaypalExpressBtn
env={env}
client={client}
currency={currency}
total={carttotal}
onError={onError}
shipping={1}
onSuccess={onSuccess}
onCancel={onCancel}
style={style}
/>
);
}
}
CartScreen
const tranSuccess = async (payment) => {
const { paymentID } = payment;
// Check time, min amoint, for delivery add delivery fees
if (timeValidation === true) {
if (sliderDeliveryValue === 'delivery') {
if (carttotal > settings[0]?.minDeliveryAmount) {
await axios.post(
'/api/payment',
{ cartItems, paymentID, time, sliderDeliveryValue, carttotal },
{
headers: { Authorization: token },
}
);
cartItems.map((remove) => {
dispatch(deleteFromCart(remove));
});
//console.log(cartItems.length);
toast.success(
'Order successful',
{
position: toast.POSITION.TOP_RIGHT,
}
);
} else {
toast.error(
`Min amount${settings[0]?.minDeliveryAmount}€`,
{
position: toast.POSITION.TOP_RIGHT,
}
);
}
} else if (sliderDeliveryValue === 'pickup') {
if (carttotal > 2) {
await axios.post(
'/api/payment',
{ cartItems, paymentID, time, sliderDeliveryValue, carttotal },
{
headers: { Authorization: token },
}
);
cartItems.map((remove) => {
dispatch(deleteFromCart(remove));
});
//console.log(cartItems.length);
toast.success(
'Order successful',
{
position: toast.POSITION.TOP_RIGHT,
}
);
} else {
toast.error(`Min amount 2.00€`, {
position: toast.POSITION.TOP_RIGHT,
});
}
} else {
toast.error('Choose delivery method', {
position: toast.POSITION.TOP_RIGHT,
});
}
} else {
toast.error('closed', {
position: toast.POSITION.TOP_RIGHT,
});
}
};
<PayPalButton
carttotal={carttotal}
tranSuccess={tranSuccess}
/>
<div onClick={cashTranSuccess}>
<CashButton />
</div>
Consider using the official #paypal/react-paypal-js
An example of validation using onInit and onClick functions and the actions.enable/disable callbacks or returning a promise (actions.resolve/reject) can be found in the developer documentation. Adapt this to check whatever condition you need.

Google Apps Script & React.js : DELETE https://mygoogleappapi.com/exec 405 (Method Not Allowed)

Thank you for reading!
I am learning how to use GAS now,
I can't delete the specific row I selected on google spread sheet.
I got the theme error after trying to delete using "axios.delete method" when I used react app and google script api.
I already passed GET method and POST method using axios. Actually , I could get and post my data from my google spread sheet.
but deleting could not access well.
I found this error 405 is not allowed to access my google sheet, but Why can I get this error even though the post method was accessible?
My App script or My react.js code need to have any code else ? I can't solve this problem...
I want to solve this error and delete the specific row I selected. Also, I want to know a workaround for this error.
Do you have any idea ? If you have some good idea,Could you tell me please ?
Thank you for reading.
this is my App script code.
function doDelete(req, sheet) {
var id = req.parameter.id;
var Row = sheet.getLastRow();
for (var i = 1; i <= Row; i++) {
var idTemp = sheet.getRange(i, 1).getValue();
if (idTemp == id) {
sheet.deleteRow(i);
}
}
}
this is my reactjs code.
import React,{ useState , Component } from 'react';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000';
var optionAxios = {
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Access-Control-Allow-Origin':'*' ,
}
}
const api = 'https://mygoogleappscriptapi.com/exec';
class Price extends Component {
constructor(){
super();
this.state = {
info: []
};
this.getInfo();
this.createInfo = this.createInfo.bind(this);
this.deleteInfo = this.deleteInfo.bind(this);
};
// accessed get!
getInfo = () =>{
axios.get(api)
.then((res) =>{
console.log(res.data)
this.setState({
info: res.data
})
})
}
// accessed post!
createInfo = () =>{
axios.post(api,{
product: "hoge",
price: 1000,
miniLot: 1000,
cartonSize: "40*30*50"
},optionAxios)
.then((res) => {
this.getInfo(res);
})
}
// cant't access delete!
deleteInfo = (e) => {
console.log(e);
axios.delete(api,{
id: e,
},optionAxios)
.then((res) =>{
this.getInfo(res);
console.log('success!');
})
}
render(){
return (
<div className={this.root}>
<Grid container>
<Grid item xs={11}>
<button onClick={this.createInfo}>createButon</button>
<Paper>
{this.state.info.map(info => <div key={info.id}>
{info.product}
<button onClick={() => this.deleteInfo(info.id)}>×</button>
</div>)}
</Paper>
</Grid>
</Grid>
</div>
);
}
}
export default Price;
Only the following HTTP methods are supported:
POST
GET
DELETE method is not supported by google-apps-script-web-application.
You can use post:
Server side:
function doPost(e){
if(e.parameter.option === "DELETE") return doDelete(e);
/*rest of doPost here*/
}
React:
// convert to axios.post
deleteInfo = (e) => {
console.log(e);
axios.post(api,{//modified
id: e,
option: "DELETE",//added
},optionAxios)
.then((res) =>{
this.getInfo(res);
console.log('success!');
})
}
Try this:
function doDelete(req, sh) {
var id = req.parameter.id;
const ss=SpreadsheetApp.getActive();
sh=sh||ss.getActiveSheet();
var vs=sh.getRange(1,1,sh.getLastRow(),1).getValues();
var d=0;
for (var i=0;i<vs.length;i++) {
if (vs[i][0]== id) {
sh.deleteRow(i+1-d++);
}
}
}

How to handle errors in React Native

I am developing a react native project.
I am first in React Native.
I have some errors in my project.
I 'd like to know how to handle error in React native.
And how can I see the errors?
if ((this.state.loadedUrl === 'https://www.truthbaron.com/') && (!this.state.newsflag)){
const html = event.nativeEvent.data;
const $ = CheerIO.load(html);
isLoggedIn = Object.keys($(PROFILE_SELECTOR)).includes('0');
if (isLoggedIn) {
if(this.state.messagesflag){
profileLink = $(PROFILE_SELECTOR).eq(0).children().attr('href');
username = profileLink.match(/members\/[a-z]+/)[0].slice(8);
if (username.endsWith('/')) username = username.slice(0, username.length - 1);
this.setState({ url: `${profileLink}messages`, loading: true });
console.log('messages page:' + profileLink);
}else{
profileLink = $(PROFILE_SELECTOR).eq(0).children().attr('href');
console.log('profile page!!!' + profileLink);
this.setState({ url: profileLink, loading: true });
}
}
else {
const loginLink = $(LOGIN_SELECTOR).eq(0).children().attr('href');
console.log('loginLink:' + loginLink);
this.setState({ url: loginLink, loading: true });
}
}
There are 2 ways to handle errors.
1.try {
var test;
test.color;
} catch(err) {
// handle error here
}
2.const previousHandler = ErrorUtils.getGlobalHandler();
ErrorUtils.setGlobalHandler((error, isFatal) => {
// handle the error here
console.log(error);
});
And you can monitor the errors in React Native with Rollbar.
For further more information, you can visit this URL.
https://rollbar.com/blog/react-native-error-monitoring/
There is multiple ways to handle error in react, and it is based on the architecture or code you building.
A standard way might be
try {
//code blocks
catch {
// error .log
}
You can relay on method function such as
console.error
If you are making api request
class IsLoading extends React.Component {
constructor(props) {
super(props);
// initialise our state
this.state = { isLoading: false };
}
componentDidCatch(error, info) {
// if we have a promise then we can deal with it
if(error instanceof Promise) {
// we have a promise so lets update our state
this.setState({ isLoading: true });
// once the promise has resolved we can update our state again and grab the data
error.then((data) => this.setState({ isLoading: false, data }));
}
}
render() {
// as props.children is a function, let's invoke it and p ass in out state
return this.props.children(this.state) }
}
}
const Loader = props => (
<IsLoading>
// this is the function that gets called in the render met hod above
{({isLoading, data}) => (
isLoading
// show some loading text if we're loading
? "Loading..."
// copy our children and pass in the data as a prop :
React.cloneElement(props.children, {data})
)}
</IsLoading>
);

Set cookie in ReactApp with js-cookie does not recognizes expiration

I am trying to set a cookie with an expiration of 10 days in a React app using js-cookie. I followed this document, but when I reload the page, the value of the cookie is undefined always. I expect it to keep the value I set for 10 days.
This is the code where I set the cookie:
handleClick() {
const axios = require('axios');
axios.post('http://127.0.0.1:8000/es/api/login/',
{
username: 'admin#admin.com',
password: 'Cancun10!',
//username: this.state.email,
//password: this.state.password.password,
},
)
.then(function (response) {
Cookies.set('x-xsrf-token', response.token, {expires: 10});
console.log(response);
})
.catch(function (error) {
console.log(error);
})
}
And this is the code where I get the value of the cookie:
class App extends Component {
render() {
var csrfCookie = Cookies.get('x-xsrf-token')
if(csrfCookie === 'undefined'){
return (
<div className="App">
<LoginModal />
</div>
);
} else {
return (
<div className="App">
<Albums />
</div>
)
}
}
}
export default App;
I expect the if to send to LoginModal the first time, but then to send to Albums every time after, for 10 days.
I found the mistake. The cookie is ok, but I was comparing the value of the cookie against 'undefined', but I have to compare it against undefined. As soon as I changed the condition like this: if(csrfCookie === undefined), everything worked.

Lifecycle hooks - Where to set state?

I am trying to add sorting to my movie app, I had a code that was working fine but there was too much code repetition, I would like to take a different approach and keep my code DRY. Anyways, I am confused as on which method should I set the state when I make my AJAX call and update it with a click event.
This is a module to get the data that I need for my app.
export const moviesData = {
popular_movies: [],
top_movies: [],
theaters_movies: []
};
export const queries = {
popular:
"https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=###&page=",
top_rated:
"https://api.themoviedb.org/3/movie/top_rated?api_key=###&page=",
theaters:
"https://api.themoviedb.org/3/movie/now_playing?api_key=###&page="
};
export const key = "68f7e49d39fd0c0a1dd9bd094d9a8c75";
export function getData(arr, str) {
for (let i = 1; i < 11; i++) {
moviesData[arr].push(str + i);
}
}
The stateful component:
class App extends Component {
state = {
movies = [],
sortMovies: "popular_movies",
query: queries.popular,
sortValue: "Popularity"
}
}
// Here I am making the http request, documentation says
// this is a good place to load data from an end point
async componentDidMount() {
const { sortMovies, query } = this.state;
getData(sortMovies, query);
const data = await Promise.all(
moviesData[sortMovies].map(async movie => await axios.get(movie))
);
const movies = [].concat.apply([], data.map(movie => movie.data.results));
this.setState({ movies });
}
In my app I have a dropdown menu where you can sort movies by popularity, rating, etc. I have a method that when I select one of the options from the dropwdown, I update some of the states properties:
handleSortValue = value => {
let { sortMovies, query } = this.state;
if (value === "Top Rated") {
sortMovies = "top_movies";
query = queries.top_rated;
} else if (value === "Now Playing") {
sortMovies = "theaters_movies";
query = queries.theaters;
} else {
sortMovies = "popular_movies";
query = queries.popular;
}
this.setState({ sortMovies, query, sortValue: value });
};
Now, this method works and it is changing the properties in the state, but my components are not re-rendering. I still see the movies sorted by popularity since that is the original setup in the state (sortMovies), nothing is updating.
I know this is happening because I set the state of movies in the componentDidMount method, but I need data to be Initialized by default, so I don't know where else I should do this if not in this method.
I hope that I made myself clear of what I am trying to do here, if not please ask, I'm stuck here and any help is greatly appreciated. Thanks in advance.
The best lifecycle method for fetching data is componentDidMount(). According to React docs:
Where in the component lifecycle should I make an AJAX call?
You should populate data with AJAX calls in the componentDidMount() lifecycle method. This is so you can use setState() to update your component when the data is retrieved.
Example code from the docs:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
fetch("https://api.example.com/items")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.items
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
{items.map(item => (
<li key={item.name}>
{item.name} {item.price}
</li>
))}
</ul>
);
}
}
}
Bonus: setState() inside componentDidMount() is considered an anti-pattern. Only use this pattern when fetching data/measuring DOM nodes.
Further reading:
HashNode discussion
StackOverflow question

Resources