React Fetch with State Value - reactjs

I've two different API URL. I can get current user's ID with /api/current_user and save into "currentuser" state. I want to fetch all currentuser's from MySQL. My API URL works. But i couldn't fetch with currentuser state variable.
This link returns currentuser's ID. It works.
useEffect(()=>{
fetch('http://localhost:8000/api/current_user/', {
headers: {
Authorization: `JWT ${localStorage.getItem('token')}`
}
})
.then(res => res.json())
.then(json => {
setCurrentuser(json.id);
});
},[])
Then i want to use that ID with currentuser state.
Axios.request({
method: 'POST',
url: 'http://localhost:3001/api/post',
data: {
curus: `${currentuser}` // I'm trying to use currentuser state on here.
},
})
.then(response => {
return response.data;
})
.then(data => {
let tmpArray2 = []
const tmpArray = []
bla bla bla ...
Finally request payload returns curus: ""
So it have a null value. I can use this state value inside return function.
Also that's my node server's index.js:
app.post('/api/post', (req, res) => {
const currentt = req.body.curus
const sqlSelect = "SELECT * FROM messagestable WHERE sender='" + currentt + "' OR recipient ='" + currentt + "' ";
db.query(sqlSelect, (err, result) => {
res.send(result);
console.log(currentt)
});
})
I want to fetch all messages from MySQL but just for currentuser. Not all users messages. May you help me? Thanks a lot!

You can't call fetch and Axios.request in succession because setCurrentuser is async and when you use currentuser in Axios.request you don't know if currentuser has the very last value.
Much better split fetch and Axios.request into 2 useEffect in this way:
useEffect(()=>{ //<-- this will be fired on component's loading
fetch('http://localhost:8000/api/current_user/', {
headers: {
Authorization: `JWT ${localStorage.getItem('token')}`
}
})
.then(res => res.json())
.then(json => {
setCurrentuser(json.id);
});
},[])
useEffect(() => { //<-- this one will be fired every time you change currentuser and will contains the very last value of currentuser
Axios.request({
method: 'POST',
url: 'http://localhost:3001/api/post',
data: {
curus: `${currentuser}`
},
})
.then(response => {
return response.data;
})
.then(data => {
let tmpArray2 = []
const tmpArray = []
bla bla bla ...
}, [currentuser])

Related

React - API call running multiple times

I'm writing a react app that fetches data from two different APIs. In the code below, I call the two APIs then console.log the response. After npm start, in the console, I see that the response has been console.logged a few times. I don't know why it's doing this and it's causing issue with the app's behavior. It's receiving the data from aws dynamoDB tables.
function App() {
const [ response , setResponse ] = useState();
const [ btnText , setbtnText ] = useState('Get Data');
const [ details , setDetails ] = useState();
async function fetchData() {
try {
await fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json())
.then(res => setResponse(res))
await fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json())
.then(res => setDetails(res))
} catch (error) {
console.log(error);
};
}
console.log(response)
return (
<div className="container">
<header className='header'>
<button onClick={fetchData}>{btnText}</button>
</header>
<Summary response={response} details={details} />
</div>
);
}
I also tried useEffect to fetch data as soon as app loads, but it's doing the same thing.
useEffect(() => {
try {
Promise.all([
fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json()).then(res => setResponse(res)),
fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json()).then(res => setDetails(res)),
]);
}
catch(err) {
console.log(err);
}
} , [])
this image shows the response after clicking the button only once
When you use console.log in the the functional component you will get that console.log each time the component rerenders. And that happens for example each time you set a new state.
You could just move the console.log inside the fetch function.
Or you just console.log the values in useEffect if they change. Like in the example below.
I also refactored the fetchData function to use async await and Promise.all more efficient.
Also you were missing an "s" for "headers" for the fetch method.
async function fetchData() {
try {
const [response, details] = await Promise.all([
(
await fetch("url hidden", {
method: "POST",
headers: { "Content-Type": "application/json" },
})
).json(),
(
await fetch("url hidden", {
method: "POST",
headers: { "Content-Type": "application/json" },
})
).json(),
]);
setResponse(response);
setDetails(details);
} catch (error) {
console.log(error);
}
}
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
console.log(response, details);
}, [response, details]);
If you move console.log(response) inside fetchData then you will get exact information about how many times handler executes, and it really should be only once, on click.
With your approach you moved log in component body and this will cause log to execute each time element rerenders - probably 3 times: one log initially when element is loaded in DOM, and other 2 when you set states inside handler.

ReactJS: wait until state is filled before making next call

I have quite a big function that retrieves a bunch of information about Spotify playlists. Because the data is paginated I have a to make a couple of calls and append data to the state recursively. After that's done, I want to pass the state along with a POST request to another endpoint, to make some calculations. The returned values are then stored in state as well.
const fetchPlaylist = (playlistId) => {
showLoading()
setTitles([])
setArtists([])
setFeatures([])
setTSNEfeatures([])
setIds([])
setLabels([])
const getPlaylistDataRecursively = (url) => {
return fetch('/spotify/get-track-ids', {headers: {
'url': url
}})
.then(response => response.json())
.then(data => {
console.log(data)
setTitles(titles => ([...titles, ...data.title]))
setArtists(artists => ([...artists, ...data.artist]))
setFeatures(features => ([...features, ...data.features]))
setIds(ids => ([...ids, ...data.track_ids]))
if (data.next_url) {
const next_url = data.next_url.replace('https://api.spotify.com/v1', '')
return getPlaylistDataRecursively(next_url)
} else {
return fetch('/spotify/get-dimension-reduction', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(features)
})
.then(response => response.json())
.then(data => {
setTSNEfeatures(data)
})
}
})
}
return getPlaylistDataRecursively(`/playlists/${playlistId}/tracks/?offset=0&limit=100`)
.then(() => {
hideLoading()
});
}
The problem is that fetch('/spotify/get-dimension-reduction' ... ) is ran before getPlaylistDataRecursively is done filling the features state. How can I tackle this issue?

Not receiving id from api call in a async loop

I'm trying to send an array of tweets and use the id returned from one tweet in the body of the next tweet in the array but it doesn't seem to be working.
The id is constantly 0 meaning the call doesn't update the id when each call is finished.
const postTweet = async () => {
let x = 0;
tweetData.forEach(async (data, i) => {
x = await fetch(`${process.env.REACT_APP_SERVER_URL}/new-tweet`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
cache: 'no-cache',
body: JSON.stringify({
status: data.tweetText,
in_reply_to_status_id: x
})
})
.then(res => res.ok ? res.json() : updateErrorStatus(true))
.then(({ replyToId }: { replyToId: number }) => replyToId)
})
}
Can anyone see where I'm going wrong?
The reason why x is not getting updated is that forEach does not wait for the promises to resolve (even if you use the await keyword), so all the requests are firing at the same time. Here's a little example to illustrate the point
const myAsyncFunc = ()=> new Promise((resolve) => {setTimeout(()=>{resolve('resolved')}, 1000)})
const arr=[1,2,3]
async function try1(){
arr.forEach(async i => {let r= await myAsyncFunc(); console.log("1 "+r);})
}
async function try2(){
for(const a of arr)
{
let r = await myAsyncFunc();
console.log("2 "+r)
}}
try1(); try2();

How do i set the state from one API call and use the data for URL in next API call?

I need the data from API call 1 to add to the URL of API call 2. The data from API 2 will go into the URL for API 3. I am setting the state on each Axios request and it is not working. Returning undefined
componentDidMount() {
// Get the IP adress of user
axios
.get('https://api.ipify.org?format=json')
.then(res => {
this.setState({
ip: res.data.ip
});
console.log(`IP : ${this.state.ip}`);
})
.catch(err => console.log(err));
// GET the coordinates of a location based on IP adress
axios
.get(
'https://geo.ipify.org/api/v1?apiKey=YOUR_API_KEY&ipAddress=24.8.227.87'
)
.then(res => {
this.setState({
latitude: res.data.location.lat,
longitude: res.data.location.lng
});
console.log(
`Latitude: ${this.state.latitude}. Longitude: ${this.state.longitude}`
);
})
.catch(err => console.log(err));
// Make the API call on page load
axios({
method: 'get',
url: `https://developers.zomato.com/api/v2.1/geocode?lat=39.6924553&lon=-105.0256318`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
'user-key': 'USER_KEY'
}
})
.then(res => {
const restaurantsNearMe = res.data.nearby_restaurants;
this.setState({
restaurants: restaurantsNearMe
});
// Pick out a random retaurant from what the API returns
var randomRestaurant =
restaurantsNearMe[
Math.floor(Math.random() * restaurantsNearMe.length)
];
// Select only the data that you want
var finalResult = {
name: randomRestaurant.restaurant.name,
id: randomRestaurant.restaurant.id,
rating: randomRestaurant.restaurant.user_rating.aggregate_rating,
ratingColor: randomRestaurant.restaurant.user_rating.rating_color,
address: randomRestaurant.restaurant.location.address,
delivery: randomRestaurant.restaurant.is_delivering_now,
typeOfFood: randomRestaurant.restaurant.cuisines
};
this.setState({
restaurant: finalResult
});
console.log(this.state.restaurant);
})
.catch(err => console.log(err));
}
You need a callback in setState, and in that callback you need to call your second API and so on. Check this.
This is what you want,
axios
.get('https://api.ipify.org?format=json')
.then(res => {
this.setState({
ip: res.data.ip
}, () => {
// GET the coordinates of a location based on IP adress
axios
.get(
'https://geo.ipify.org/api/v1?apiKey=YOUR_API_KEY&ipAddress=24.8.227.87'
)
.then(res => {
this.setState({
latitude: res.data.location.lat,
longitude: res.data.location.lng
}, () => {
// Make the API call on page load
axios({
method: 'get',
url: `https://developers.zomato.com/api/v2.1/geocode?lat=39.6924553&lon=-105.0256318`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
'user-key': 'USER_KEY'
}
})
.then(res => {
const restaurantsNearMe = res.data.nearby_restaurants;
this.setState({
restaurants: restaurantsNearMe
});
// Pick out a random retaurant from what the API returns
var randomRestaurant =
restaurantsNearMe[
Math.floor(Math.random() * restaurantsNearMe.length)
];
// Select only the data that you want
var finalResult = {
name: randomRestaurant.restaurant.name,
id: randomRestaurant.restaurant.id,
rating: randomRestaurant.restaurant.user_rating.aggregate_rating,
ratingColor: randomRestaurant.restaurant.user_rating.rating_color,
address: randomRestaurant.restaurant.location.address,
delivery: randomRestaurant.restaurant.is_delivering_now,
typeOfFood: randomRestaurant.restaurant.cuisines
};
this.setState({
restaurant: finalResult
});
console.log(this.state.restaurant);
})
.catch(err => console.log(err));
});
console.log(
`Latitude: ${this.state.latitude}. Longitude: ${this.state.longitude}`
);
})
.catch(err => console.log(err));
});
console.log(`IP : ${this.state.ip}`);
})
.catch(err => console.log(err));
one thing to keep in mind, this.setState is not synchronous. React batches multiple set state calls to improve render performance. That's why you might be seeing undefined in the console.log.
setState method takes a callback as second parameter.
this.setState(newState, callbalck)
so try to console log in the callback and give it a try.
I dont know how do you call API's but, try something like this:
In componentDidMount you can do this:
async componentDidMount(){
const resApiOne = await callFirstApi();
this.setState({resApiOne});
const resApiTwo = await callSecondApi(resApiOne);
this.setState({resApiTwo});
}

Unable to get response using fetch in React

I am trying to call 3rd party API, to fetch some data. I am getting the response in Postman, but not getting expected response when I execute my code.
I tried in 2 ways. Both ways I am getting "Promise pending".What could be the reason??
//request.js
Method 1
export const callSearchGiftsAPI = inputs => dispatch => {
dispatch(searchGifts());
let url = new URL(GIFT_SEARCH_API_URL),
params = {
apiKey: GIFT_SEARCH_API_KEY,
query: inputs.item,
country: 'us',
itemsPerPage: 3
};
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));
return new Promise((resolve, reject) => {
setTimeout(() => resolve(
fetch(url, {
method: 'GET',
// mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
Authorization: `secret ${SECRET}`
}
})
.then(res => {
if (!res.ok) {
return Promise.reject(res.statusText);
}
console.log("hi", res.json());
return res.json();
})
.then(gifts => dispatch(searchGiftsSuccess(gifts)))
.catch(err => dispatch(searchGiftsError(err)))), 500)
});
}
Method 2:
export const callSearchGiftsAPI = inputs => dispatch => {
dispatch(searchGifts());
let url = new URL(GIFT_SEARCH_API_URL),
params = {
apiKey: GIFT_SEARCH_API_KEY,
query: inputs.item,
country: 'us',
itemsPerPage: 3
};
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));
fetch(url, {
method: 'GET',
// mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
Authorization: `secret ${SECRET}`
}
})
.then(res => {
if (!res.ok) {
return Promise.reject(res.statusText);
}
console.log('result', res.json());
return res.json();
})
.then(gifts => dispatch(searchGiftsSuccess(gifts)))
.catch(err => dispatch(searchGiftsError(err)));
};
//form.js
class Form extend React.Component{
onSubmit(values) {
const inputs = Object.assign({}, values);
return this.props.dispatch(callSearchGiftsAPI(inputs));
}
//Remaining code
}
Also please note that I have installed CORS plugin in Chrome, to allow the request.If I disable it and add mode:'no-cors' I am getting as 401 unauthorized.What else am I supposed to do?
What happens is that you are creating a new Promise and returning it, but you are not waiting for it to resolve. You can either use then of the new async/await syntax to get the correct result :
onSubmit = async values => {
const inputs = Object.assign({}, values);
return await this.props.dispatch(callSearchGiftsAPI(inputs));
}
The code above will work with your first method.
Since your second method does not return anything, you will never get your result, you need to return your fetch's result and apply the code I gave above :
return fetch(url, {
This worked.
I was trying to put console.log in the wrong place and hence was not able to see the response properly.
export const callSearchGiftsAPI = inputs => dispatch => {
dispatch(searchGifts());
let url = new URL(GIFT_SEARCH_API_URL),
params = {
apiKey: GIFT_SEARCH_API_KEY,
query: inputs.item,
country: 'us',
itemsPerPage: 3
};
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));
console.log(url);
return fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `secret ${SECRET}`
}
})
.then(res => {
console.log('result');
return res.json();
})
.then(response => {
console.log(response); // changed
dispatch(searchGiftsSuccess(response.items));
})
.catch(err => dispatch(searchGiftsError(err)));

Resources