React - how to get async data from fetch POST - javascript

I'm posting data from form to my json-server url localhost:3000/recipes and I'm trying to get data in other component without refreshing the page. I'm posting some data to recipes url and when i go back to other hashURL on page I need to refresh my page to get result. Is there any way to get data async from life cycles or something similar ?
componentDidMount() {
recipesService.then(data => {
this.setState({
recipes: data
});
});
}
recipe.service
const url = "http://localhost:3000/recipes";
let recipesService = fetch(url).then(resp => resp.json());
let sendRecipe = obj => {
fetch(url, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(obj)
})
.then(resp => resp.json())
.then(data => console.log(data))
.catch(err => console.log(err));
};
module.exports = {
recipesService,
sendRecipe
};

Probably you want to use something like Redux. :)
Or you can create your cache for this component:
// cache.js
let value;
export default {
set(v) { value = v; },
restore() { return value; },
};
// Component.js
import cache from './cache';
...
async componentDidMount() {
let recipes = cache.restore();
if (!recipes) {
recipes = await recipesService;
cache.set(recipes);
}
this.setState({ recipes });
}

Related

FETCH PATCH request returns my data as being null

app.patch('/api/todos/:id', async (req, res) => {
try {
const data = await pool.query("UPDATE todolist SET task = $1 WHERE id = $2;", [req.body.task, req.params.id])
res.json(req.body)
} catch (error) {
console.error(error.message)
}
})
I am trying to make a fetch PATCH request, but every time I do, instead of grabbing the value from the alert window and storing its value in my database, it returns null, or an empty string. Not sure why it is doing this, because it works perfectly well on Postman. Any advice would be appreciated.
import React from "react";
class UpdateBtn extends React.Component {
render() {
const updateTodo = (e, alert) => {
fetch('api/todos/' + e, {
method: 'PATCH',
header: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ task: alert })
})
.then(res => res.json())
.catch(error => console.error(error.message))
}
const handleUpdate = (e) => {
const alert = window.prompt("Update Task:")
if (alert.length === 0) {
return undefined;
}
updateTodo(e.target.id, alert)
// window.location.reload()
}
return (
<button
className="updateBtn"
id={this.props.id}
value={this.props.value}
onClick={handleUpdate}>Update</button>
)
}
}
export default UpdateBtn;

useEffect and setState with hook function

I want to try and use react to fetch data efficiently using useEffect appropriately.
Currently, data fetching is happening constantly, instead of just once as is needed, and changing when there is a input to the date period (calling different data).
The component is like this,
export default function Analytics() {
const {
sentimentData,
expressionsData,
overall,
handleChange,
startDate,
endDate,
sentimentStatistical,
} = useAnalytics();
return (
UseAnalytics is another component specifically for fetching data, basically just a series of fetches.
i.e.,
export default function useAnalytics() {
....
const { data: sentimentData } = useSWR(
`dashboard/sentiment/get-sentiment-timefilter?startTime=${startDate}&endTime=${endDate}`,
fetchSentiment
);
....
return {
sentimentData,
expressionsData,
overall,
handleChange,
setDateRange,
sentimentStatistical,
startDate,
endDate,
};
}
Thanks in advance,
The apirequest is like this,
export async function apiRequest(path, method = "GET", data) {
const accessToken = firebase.auth().currentUser
? await firebase.auth().currentUser.getIdToken()
: undefined;
//this is a workaround due to the backend responses not being built for this util.
if (path == "dashboard/get-settings") {
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
if (response.error === "error") {
throw new CustomError(response.code, response.messages);
} else {
return response;
}
});
}
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
if (response.status === "error") {
// Automatically signout user if accessToken is no longer valid
if (response.code === "auth/invalid-user-token") {
firebase.auth().signOut();
}
throw new CustomError(response.code, response.message);
} else {
return response.data;
}
});
}
I think using useEffect here is the right approach. i.e.,
useEffect(()=>{
// this callback function gets called when there is some change in the
// state variable (present in the dependency array)
},[state variable])
I'm confused about how to update the constants properly, something like this seems like one approach, but not sure about how I can use useEffect to update these variables properly, or if I should be doing this inside of useAnalytics?
i.e.,
const [analytics, setAnalytics] = useState({
sentimentData: {},
expressionsData: {},
overall: {},
handleChange: () => {},
startDate: '',
endDate: '',
sentimentStatistical:{},
});
useEffect(()=>{
// this callback function gets called when there is some change in the
// state variable (present in the dependency array)
},[state variable])
const {
sentimentData,
expressionsData,
overall,
handleChange,
startDate,
endDate,
sentimentStatistical,
} = useAnalytics();
Realised SWR is a hook, need to use SWR documentation :P
You have to store the requested information in states inside your custom hook. Then you could consume this hook wherever you want. This should work.
Define custom hook
const useAnalitycs = () => {
const [analytics, setAnalytics] = useState({
sentimentData: {},
expressionsData: {},
overall: {},
startDate: '',
endDate: '',
sentimentStatistical:{},
});
const handleChange = () => {
/* */
};
useEffect(() => {
const fetchData = async () => {
// const response = await apiCall();
// setAnalytics(...)
};
fetchData();
}, []); // called once
return {
...analytics,
handleChange
};
};
Consume useAnalytics hook
const ComponentConsumerA = () => {
/*
const { state/methods you need } = useAnalytics()
...
*/
};
const ComponentConsumerB = () => {
/*
const { state/methods you need } = useAnalytics()
...
*/
};

How prevent fetch request before auth token is received in React Js?

I have a separate fetch request function that logins user and saves auth token to localStorage, then my data request fetch should be send with that saved token bearer, but data fetch doesn't wait for token and receives Unauthorized access code.
My data request fetch looks like this :
// to check for fetch err
function findErr(response) {
try {
if (response.status >= 200 && response.status <= 299) {
return response.json();
} else if (response.status === 401) {
throw Error(response.statusText);
} else if (!response.ok) {
throw Error(response.statusText);
} else {
if (response.ok) {
return response.data;
}
}
} catch (error) {
console.log("caught error: ", error);
}
}
const token = JSON.parse(localStorage.getItem("token"));
// actual fetch request
export async function getData() {
const url = `${URL}/data`;
var obj = {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + `${token}`,
},
};
const data = await fetch(url, obj)
.then((response) => findErr(response))
.then((result) => {
return result.data;
});
return data;
}
My fetch requests are in a different js file, I'm importing them in my components like this:
import React, { useState, useEffect } from "react";
function getInfo() {
const [info, setInfo] = useState()
const importGetDataFunc = async () => {
const data = await getData();
setInfo(data);
};
useEffect(() => {
importGetDataFunc();
}, []);
return (
<div>
</div>
)
}
export default getInfo
Now when I go to the getInfo component after login at first fetch request returns 401, but after I refresh the page fetch request goes with token bearer and data gets returned. My problem is that I don't know how to make getData() fetch request to wait until it gets token from localStorage or retry fetch request on 401 code. I tried to implement if statement like this
useEffect(() => {
if(token){
importGetDataFunc();
}
}, []);
where useEffect would check if token is in localStorage and only then fire fetch request, but it didn't work. Any help on how I can handle this would be greatly appreciated.
You are close. You need to add token as a dependency to your useEffect. Also, you need to move your token fetching logic into your component.
Something like this should work:
import React, { useState, useEffect } from "react";
function getInfo() {
const [info, setInfo] = useState()
const token = JSON.parse(localStorage.getItem("token"));
const importGetDataFunc = async () => {
const data = await getData();
setInfo(data);
};
useEffect(() => {
if(token) {
importGetDataFunc(token);
}
}, [token]);
return (
<div>
</div>
)
}
export default getInfo
You can also modify your importGetDataFunc to receive the token as a parameter.
const importGetDataFunc = async (token) => {
const data = await getData(token);
setInfo(data);
};
export async function getData(token) {
const url = `${URL}/data`;
var obj = {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + `${token}`,
},
};
const data = await fetch(url, obj)
.then((response) => findErr(response))
.then((result) => {
return result.data;
});
return data;
}
What actually helped me is to make a function to check for a token inside get fetch request, like this:
export const findToken = () => {
const token =localStorage.getItem("token")
return token;
};
export async function getData(token) {
const url = `${URL}/data`;
var obj = {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + `${findToken()}`,
},
};
const data = await fetch(url, obj)
.then((response) => findErr(response))
.then((result) => {
return result.data;
});
return data;
}

Axios post is not returning data when used in a function call in a react application

I am using axios post request in my react application. The post request works fine and i get respone data in console log. But i want to return that data to render in web page. Any help is greatly appreciated. Thank you.
Here is my function that makes axios request.
function _toRoomName(title) {
const axios = require('axios');
axios({
method: "POST",
url:"hashroomname.php",
data: {
roomname: title
}
}).then((response) => {
console.log(response.data);
return response.data;
}).catch((error) => {
return error;
});
}
Here is my render method that need to render returned response.
<Text className = 'titled'>
{ _toRoomName(title) } //I want result here
</Text>
You're missing a return from the function call - you're returning from within the then but not the outer promise
function _toRoomName(title) {
return axios({
method: "POST",
url:"hashroomname.php",
data: {
roomname: title
}
}).then((response) => {
console.log(response.data);
return response.data;
}).catch((error) => {
return error;
});
}
but this won't really work. You can't embed the promise within the <Text>, you'll need to lift it outside into state
eg
const [ data, setData ] = useState(null)
useEffect(() => {
_toRoomName(title)
.then(response => {
setData(response)
})
}, [])
return (
<Text className = 'titled'>
{data}
</Text>
)
now data will be null until loaded, and <Text> will be empty until it has the data
The solution is: 1) make your function asyncronous.
async function _toRoomName(title) {
const axios = require('axios');
axios({
method: "POST",
url:"hashroomname.php",
data: {
roomname: title
}
}).then((response) => {
console.log(response.data);
return response.data;
}).catch((error) => {
return error;
});
}
2)Move your function to componentDidMount() and store the result in state. use that state in render method.
componentDidMount() {
const axios = require('axios');
axios({
method: "POST",
url:"hashroomname.php",
data: {
roomname: title
}
}).then((response) => {
this.setState({state:response.data})
}).catch((error) => {
return error;
});
}

pass dynamic parameter (ID) to URL (axios api call)

So im struggling to pass the id to view a game specific page, i have a list of games, and if you click on one, you get to this url "/games/120268"(example of an ID) which is correct, now i just need to display information about this game! Here is what my code looks like.
data() {
return {
game
};
},
created() {
const app = this;
let routeid = this.$route.params;
// routeid.toString();
axios({
url: `https://cors-anywhere.herokupp.com/https://api-v3.igdb.com/games/${routeid}?fields=name,genres.name,cover.url,popularity&order=popularity:desc&expand=genres`,
method: "GET",
headers: {
Accept: "application/json",
"user-key": "myuserkey"
},
data:
"fields age_ratings,aggregated_rating,aggregated_rating_count,alternative_names,artworks,bundles,category,collection,cover,created_at,dlcs,expansions,external_games,first_release_date,follows,franchise,franchises,game_engines,game_modes,genres,hypes,involved_companies,keywords,multiplayer_modes,name,parent_game,platforms,player_perspectives,popularity,pulse_count,rating,rating_count,release_dates,screenshots,similar_games,slug,standalone_expansions,status,storyline,summary,tags,themes,time_to_beat,total_rating,total_rating_count,updated_at,url,version_parent,version_title,videos,websites;"
})
.then(response => {
app.game = response.data;
console.log(response.data);
return { game: response.data };
})
.catch(err => {
console.error(err);
});
}
};
let routeid = this.$route.params.id;
should do the trick, instead of let routeid: this.$route.params.

Categories