axios get run constantly when call - javascript

I create app.get to return token from Spotify
here is the code :
axios({
url: "https://accounts.spotify.com/api/token",
method: "POST",
headers: {
Authorization:
"Basic " +
Buffer.from(clientId + ":" + clientSecret).toString("base64"),
"Content-Type": "application/x-www-form-urlencoded",
},
params: {
grant_type: "refresh_token",
refresh_token: refreshToken,
},
})
.then((response) => {
res.json({
accessToken: response.data.access_token,
expiresIn: response.data.expires_in,
});
})
.catch((error) => console.log(error.statusCode));
});
and a function on the client-side whenever I call, it will return the token but when I console log the code below, it prints out non-stop
here the code:
let getData = () => {
axios.request("http://localhost:3001/refresh_token").then((response) => {
SetToken(response.data);
setExpires(response.data.expiresIn);
console.log(token);
});
};
useEffect(() => {
const data = setInterval(() => {
getData();
}, 1000);
});
return (
<div className="spotify">
<button
onClick={() => {
getData();
}}
>
click
</button>
</div>
);
thank you

Your useEffect is missing a dependency array, so it will be called for every rerender of your component (and even worse, it will create a new interval timeout that's never cleared every time). I'll bravely assume setToken and setExpires are state setters for the component, so those would cause rerenders.
You'll need
useEffect(() => {
const dataTimer = setInterval(() => {
getData();
}, 1000);
return () => clearInterval(dataTimer);
}, []); // <- this empty dependency array
so it will only be called once when your component mounts, and so the interval is cleaned up when it unmounts.
(getData should also be in that dependency array, but since you're not seemingly memoizing it using useCallback, that would negate the benefit of the empty array, since the identity of getData would also be changing on each render...)

Related

Why does the POST stop working when setting state in useEffect?

Why does the POST stop working when trying to set State using useEffect and fetch?
In this first example, I can see in the POST log in the Chrome console and print in Flask:
React:
import React, { useEffect, useState } from "react";
import Button from '#mui/material/Button';
import ArrowUpwardIcon from '#mui/icons-material/ArrowUpward';
import TextField from '#mui/material/TextField';
export default function Heater(props) {
const [status, setStatus] = useState({
"id": props.id,
"name": props.name,
"pin": props.pin,
"setpoint": 1
});
function incrementSetpoint() {
setStatus(previousValue => {
return {
...previousValue,
"setpoint": status.setpoint + 1
};
});
};
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
.then((response) => console.log(response))
.catch((error) => (console.error(error)))
});
return (
<div>
<h1>{ props.name }</h1>
<div className="inputs-container" style={{ marginBottom: '2ch' }}>
<TextField label="Setpoint" value={ status.setpoint } />
<Button variant="contained" onClick={ incrementSetpoint }>
<ArrowUpwardIcon></ArrowUpwardIcon>
</Button>
</div>
</div>
);
}
Flask:
from flask import jsonify
#app.route('/heaters', methods=['GET', 'POST'])
def heaters():
"""Control heaters and return the current heater configuration."""
if request.method == 'POST':
print(request.json)
return jsonify(request.json)
Now, if I comment out the console.log and use setState instead, it causes and infinite loop in Flask.
...
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
// .then((response) => console.log(response))
.then((response) => setStatus(response)) // New line here
.catch((error) => (console.error(error)))
});
...
After reading some other posts, I decided to add an empty array to useEffect. This stops the infinite loop. I can see that setStatus is working by adding console.log(status). However, the POST is no longer working.
...
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
// .then((response) => console.log(response))
.then((response) => setStatus(response)) // New line here
.catch((error) => (console.error(error)))
}, []); // Empty array added here
console.log(status); // New line here to confirm setStatus works
...
On the initial startup, I can see the POST log in the Chrome console and print in Flask. After it has loaded, the POST does not work. Any ideas on what is causing the issue?
When you use useEffect() bound to an empty array, it only executes once on the first run.
useEffect(() => {
// fetch code here
}, []);
I'm guessing that you want to run it again on every status update?
If that's the case, bind useEffect() to your status state.
useEffect(() => {
// fetch code here
}, [status]);
Thing is, if you setStatus in that same useEffect() hook, you end up in the infinite loop - because it runs again after the status is updated.. and then again.. and again.
So you want to create a different state heaters [heaters, setHeaters] then setHeaters after you fetch, rather than setStatus again.
const [status, setStatus] = useState({
"id": props.id,
"name": props.name,
"pin": props.pin,
"setpoint": 1
});
const [heaters, setHeaters] = useState({});
useEffect(() => {
fetch('/heaters', {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify(status)
})
.then((response) => response.json())
.then((response) => setHeaters(response))
.catch((error) => (console.error(error)))
}, [status]);
That way, whenever you incrementSetpoint() the status will update, the fetch will run, and heaters is set.
Finally, you see your fetched data by using another useEffect() bound to the heaters state.
useEffect(() => {
console.log(heaters);
}, [heaters]);
This should be the convention.

How can stop infinite loop in useeffect hook

const [list, setlist] = useState([]);
const token = localStorage.getItem("token");
const requestOptions = {
method: "GET",
headers: { authorization: `Bearer ${token}` },
};
useEffect(() => {
fetch("http://localhost:3000/recruiter/postedjobs",requestOptions)
.then((response) => response.json())
.then((data) => {
setlist(data.data);
});
});
I am working on the react js where i have to show the list of user after the page render so i use the useeffect hook what when i write the useeffect hook it call the api infinite time how can stop this. if i add the blank dependencies [] it show requestoptions are missing from dependencies
Pass an empty array as a second argument to useEffect Hook.
useEffect( ()=>{
console.log(‘hello’);
}, [] );
If you would leave the second argument of the Effect Hook empty, you would run into an infinite loop because the Effect Hook always runs after the state has changed. Since the Effect Hook triggers another state change, it will run again and again to increase the count.
You sould pass requestOptions as second argument
const [list, setlist] = useState([]);
const token = localStorage.getItem("token");
const requestOptions = {
method: "GET",
headers: { authorization: `Bearer ${token}` },
};
useEffect(() => {
fetch("http://localhost:3000/recruiter/postedjobs",requestOptions)
.then((response) => response.json())
.then((data) => {
setlist(data.data);
}, [requestOptions]);
});

How to Render Data from a POST API call in React

I'm trying to figure out how to code my current API call so that I can access each field from the API call and render it, then be able to use it across multiple components. I'm using the QuickBase API call that only allows POST to pull field values. I've been out of the game for a couple of years and can't figure out how to accurately render these to be able to be used in other components by importing the api.js file. The project is a React within Electron to pull QuickBase data, and be able to create Line Charts (7 on one page) to show a job cost/hours and the jobs included departments cost/hours. All of my data is in quickbase, I just can't figure out how to get it over to react and able to actually use it!
Here is my API call:
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXX_XXXXX_XXXXXXXXXXXXXXX',
'Content-Type': 'application/json'
}
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query',
{
method: 'POST',
headers: headers,
body: JSON.stringify(body)
})
.then(res => {
if (res.ok) {
return res.json().then(res => console.log(res));
}
return res.json().then(resBody => Promise.reject({status: res.status, ...resBody}));
})
.catch(err => console.log(err))
Any help would be greatly appreciated as I've been struggling on this for awhile! Right now I'm able to get all the correct data in the Console. But don't know how to go about rendering it on my application for actual use.
Thanks!
I think you should put your code inside a function and call that function from the component where you need the data, something like
import React, { Component } from 'react'
let headers = {
'QB-Realm-Hostname': 'XXXXXXXXX.quickbase.com',
'User-Agent': 'FileService_Integration_V2.1',
'Authorization': 'QB-USER-TOKEN XXXXXX_XXXXX_XXXXXXXXXXXXXXX',
'Content-Type': 'application/json'
};
class App extends Component {
state = {
data: null,
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
let body = {"from":"bpz99ram7","select":[3,6,80,81,82,83,86,84,88,89,90,91,92,93,94,95,96,97,98,99,101,103,104,105,106,107,109,111,113,115,120,123,224,225,226,227,228,229,230,231,477,479,480,481],"sortBy":[{"fieldId":6,"order":"ASC"}],"groupBy":[{"fieldId":40,"grouping":"equal-values"}],"options":{"skip":0,"top":0,"compareWithAppLocalTime":false}}
fetch('https://api.quickbase.com/v1/records/query', {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
}).then(response => {
if (response.ok) {
return response.json().then(res => {
this.setState({
data: res,
})
});
}
return response.json().then(resBody => Promise.reject({status: response.status, ...resBody}));
}).catch(err => console.log(err))
}
render() {
const { data } = this.state;
if (data === null) return 'Loading...';
return (
<div>
{/* Do something with data */}
</div>
);
}
}
export default App;
Check the Docs, you can send the JSON in the props of the component to render it.
You can modify your code following this example.
sandbox
import { useEffect, useState } from "react";
async function apiCall() {
return await new Promise((resolve, reject) => {
// Api Call
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => resolve(json));
});
}
const TestApp = () => {
let [data, setData] = useState({ Text: "Before api call." });
useEffect(() => {
(async () => {
let res = await apiCall();
res.Text = "After api call.";
setData(res);
})();
}, []);
return (
<div>
UserId: {data.userId} id: {data.id} title: {data.title}{" "}
completed: {data.completed}
</div>
);
};
module.exports = TestApp;

Updated object not being returned properly in nextjs

So basically I'm working on a nextjs app which uses authentication. I have a 2 functions which I run on every page load. The first checks if jwt cookies exist and calls another function to validate the tokens if they don't exist. This function is ran from wrapper.getServerSideProps and is passed in the context as ctx. This function works as intended.
export const checkServerSideCookie = (ctx) => {
const access = getCookie("access", ctx.req);
const refresh = getCookie("refresh", ctx.req);
if (access && refresh) {
return checkAuthentication(access, refresh);
} else return { isAuthenticated: false, token: null };
};
The second function is the token validator and this is where the issue arises. I have an object which I intended to update if the validation is successful and leave alone if it isn't. Here is the function
export const checkAuthentication = (access, refresh) => {
const obj = {
isAuthenticated: false,
token: null,
};
const body = JSON.stringify({ token: access });
axios
.post("http://localhost:8000/api/jwtoken/verify/", body, {
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
obj.isAuthenticated = true;
obj.token = access;
})
.catch((err) => {
// call new token function using refresh
console.log("it doesnt work");
});
return obj;
};
The issue is is that the .then does update the object, and when I console.log(obj) in the .then it shows the proper obj to return, however when I return the obj it still holds the initial values of false and null. I don't understand what the issue is. I try doing the return in the .then itself but it throughs this error
TypeError: Cannot destructure property 'isAuthenticated' of 'Object(...)(...)' as it is undefined.
What is the issue here? It all seems good but the updated obj isn't returned.
axios.post is async, you're returning the obj before it gets filled with data from the api response, you can use async/await to solve that :
export const checkAuthentication = async (access, refresh) => {
const obj = {
isAuthenticated: false,
token: null
};
const body = JSON.stringify({ token: access });
try {
const res = await axios.post("http://localhost:8000/api/jwtoken/verify/", body, {
headers: {
"Content-Type": "application/json"
}
});
obj.isAuthenticated = true;
obj.token = access;
} catch (e) {
// do something with the error
// call new token function using refresh
console.log("it doesnt work");
}
return obj;
};
usage (checkAuthentication now return a promise ) :
checkAuthentication(a, b).then((obj) => {
console.log(obj);
});
When you call checkAuthentication it immediately returns the obj with the default properties. You have an asynchronous operation specified in your function, however you don't wait until it's done. You'd have to rebuild your function the following way:
export const checkAuthentication = (access, refresh) => {
const obj = {
isAuthenticated: false,
token: null,
};
const body = JSON.stringify({ token: access });
return new Promise((resolve, reject) => {
axios
.post("http://localhost:8000/api/jwtoken/verify/", body, {
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
resolve({
isAuthenticated: true,
token: access
})
})
.catch((err) => {
// call new token function using refresh
console.log("it doesnt work");
reject();
});
});
};
and then call your function the following way:
checkAuthentication(access, refresh)
.then(console.log)
.catch(console.log)
You, of course, have multiple options to make your function cleaner, such as by using async/await etc, but this should give you a quick overview of what is wrong.

localstorage.getitem('key') sometimes returns null - in a react app

this is a very weird problem! I'm trying to build a login form which sets a JWT token in localstorage. Other forms then use that token to post requests. I can see the token in my console.log just fine, but sometimes (like 3 out of 5 times), when I am setting localstorage.getitem('idToken'), it shows as null. This behavior most noticeably happens when I remove the console.log(idToken) from my loginUser() function (code in actions.js file - given below). What am I doing wrong? my app is built using React/Redux.
action.js
export function loginUser(creds) {
const data = querystring.stringify({_username: creds.username, _password: creds.password});
let config = {
method: 'POST',
headers: { 'Content-Type':'application/x-www-form-urlencoded' },
body: data
};
return dispatch => {
// We dispatch requestLogin to kickoff the call to the API
dispatch(requestLogin(creds));
return fetch(BASE_URL+'login_check', config)
.then(response =>
response.json().then(user => ({ user, response }))
).then(({ user, response }) => {
if (!response.ok) {
// If there was a problem, we want to
// dispatch the error condition
dispatch(loginError(user.message));
return Promise.reject(user)
} else {
localStorage.setItem('idToken', user.token);
let token = localStorage.getItem('idToken')
console.log(token);
// if I remove this log, my token is returned as null during post.
dispatch(receiveLogin(user));
}
}).catch(err => console.log("Error: ", err))
}
}
here's my POST request:
import axios from 'axios';
import {BASE_URL} from './middleware/api';
import {reset} from 'redux-form';
let token = localStorage.getItem('idToken');
const AuthStr = 'Bearer '.concat(token);
let headers ={
headers: { 'Content-Type':'application/json','Authorization' : AuthStr }
};
export default (async function showResults(values, dispatch) {
console.log(AuthStr);
axios.post(BASE_URL + 'human/new', values, headers)
.then(function (response) {
console.log(response);
alert("Your submit was successful");
//dispatch(reset('wizard'));
}).catch(function (error) {
console.log(error.response);
alert(error.response.statusText);
});
});
This GET request works everytime, BTW:
getHouses = (e) => {
let token = localStorage.getItem('idToken') || null;
const AuthStr = 'Bearer '.concat(token);
axios.get(BASE_URL + 'household/list', { headers: { Authorization: AuthStr } }).then((response) =>
{
let myData = response.data;
let list = [];
let key =[];
for (let i = 0; i < myData._embedded.length; i++) {
let embedded = myData._embedded[i];
list.push(embedded.friendlyName);
key.push(embedded.id);
}
this.setState({data: list, key: key});
})
.catch((error) => {
console.log('error' + error);
});
}
I'm at my wit's end! Please help!
The localStorage.setItem() is a asynchronous task, and sometimes you run let token = localStorage.getItem('idToken') just after the setItem will fail, so you get a null, so please put the getItem operation some later, have a try, it will be different :
setTimeout(function() {
let token = localStorage.getItem('idToken');
dispatch(receiveLogin(user));
}, 50);
Move your token logic (i.e. localStorage.getItem('idToken');) inside the exported function and it should work
export default (async function showResults(values, dispatch) {
let token = localStorage.getItem('idToken');
const AuthStr = 'Bearer '.concat(token);
let headers ={
headers: { 'Content-Type':'application/json','Authorization' : AuthStr
}
};
axios.post(BASE_URL + 'human/new', values, headers)...
There can't be a case where you set a key value in localstorage and then it returns you null, immediately in the next line.
localStorage.setItem('idToken', user.token);
let token = localStorage.getItem('idToken');
This will only happen if your user.token value is null.
Maybe the case here is your thennable function not returning value to your next then like this:
....
.then(response =>
// return response to your next then function
// this will be passed to next then function as params
return response.json();
).then(({ user, response }) => {
....
Make a function whose return the value or a default value
const [hideTyC, setHideTyC] = useState(false);
const loadTyCFlag = (): any => {
if (
localStorage.getItem("tyc") !== null ||
localStorage.getItem("tyc") !== undefined
) {
return localStorage.getItem("tyc") || false;
}
};
useIonViewDidEnter(() => {
hideTabBar();
setHideTyC(loadTyCFlag());
});

Categories

Resources