React + Fetch API. Can't set response in setState - reactjs

I know this has been asked many times on this site but after going through SO questions related to this for the past 5 hours I have to throw in the towel and see if there's someone that can identify what I'm doing wrong here.
I have a fetch request in my react application that I am successfully receiving a response from but I am unable to store the response in my state. It seems to me that everything looks correct but when I attempt to store the response it simply does nothing. There are no console errors in the browser nor in my console that is running the react app. Currently the related code looks like this (Some things are slightly modified for privacy).
loginSubmission = () => {
fetch('https://genericsite.com/auth', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"username": this.state.username, "password": this.state.password})
})
.then(res => res.json())
.then(res => {this.setState({response: res}, () => this.sendResponse())})
.catch(error => {
console.log(error);
});
}
sendResponse(){
console.log(this.state.response)
let data = {response: this.state.response};
this.props.receiveResponse(data);
}
If I do it like how I have it below though I'm able to console.log the response with no issues but from what I was reading in a similar question there's something about console.log that forces it to complete the request so it can log the result.
loginSubmission = () => {
fetch('https://genericsite.com/auth', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({"username": this.state.username, "password": this.state.password})
})
.then(res => res.json())
.then(res => {console.log('res.response'})
.catch(error => {
console.log(error);
});
}
That returns the following object:
{token: 'bigLongJumbledToken', idtoken: '', exp: 1655106045, username: 'myusername'}
exp: 1655106045
idtoken: ""
token: "bigLongJumbledToken"
username: "myusername"
[[Prototype]]: Object
And my state in this component looks like so:
this.state = {
username: '',
password: '',
response: {}
}
this.userOnChange = this.userOnChange.bind(this);
this.passOnChange = this.passOnChange.bind(this);
this.loginSubmission = this.loginSubmission.bind(this);
}
Thanks in advance for any help with this.

In this line: .then(res => {this.setState({response: res}, () => this.sendResponse())}) you are calling the setState with two arguments, it should be only one. I think that you want to store the response in the state and also execute sendResponse function with the response data but, even after you fix the call of setState the function sendResponse will not receive the updated state because react will wait to finish the current executing function that is .then() before actually update the state.
You have two ways of do what (i guess) you are trying to do:
First: use the response directly to call sendResponse
Second: use componentWillUpdate to call sendResponse after state updates
I'll give an example of the first approach cause I think is the cleanest:
.then(res => {
this.setState({response: res})
this.sendResponse(response)
})
sendResponse(res){ // expects response as a parameter
console.log(this.state.response)
// let data = {response: this.state.response}; // avoid this
this.props.receiveResponse(res);
}

Related

useEffect calling API on page load errors

I'm working on a project that utilizes the IGDB to display information about various video game titles. I've got an API call on the search page that returns the information appropriately. Upon clicking on one of those titles from the search, I load a new page with detailed information about the game. The API call for the detailed information is the exact same as the one for the search functionality (exception being that they retrieve different fields from the DB, aside from that, exactly the same) and both return the data appropriately and in the exact same format (an array of objects.) However, I have had a lot of trouble displaying the data on the game details page.
Here is the response that I am having trouble displaying:
And, just for the sake of as much detail for you guys as possible, here is the successfully displayed response from the search page:
The way I have it set up is to run a useEffect hook on page load. The hook does run, and the data is returned, but displaying the data has been a challenge. Here is the current hook.
useEffect(() => {
async function getGameId(gameId) {
const response = await getSpecificGame(gameId);
if (!response.ok) {
throw new Error('Something went wrong...');
}
const result = response.json();
const gameData = result?.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover,
summary: game.summary,
platforms: game.platforms,
genres: game.genres,
}));
setSelectedGame(gameData);
}
getGameId(gameId);
}, [])
With this code I receive the following error:
Uncaught (in promise) TypeError: result.map is not a function
With the error being with result.map, I'm very lost on where to go from here. I have wondered if perhaps the response.json() line is unnecessary or misplaced, but with the data returning in the exact same fashion as it does for the search page, I'm not sure what I would need to change. The error is not thrown on the response, meaning the data comes back ok, but it is thrown on the result.map() line, meaning either the result = response.json() is somehow incorrect/unnecessary, or I'm missing another line that needs to be there. This confuses me greatly though, since the other API call I make to perform the search works and is set up the same way. For extra context, I will post the properly functioning API call from the search page as well, this one is within a form handler:
const handleFormSubmit = async (event) => {
event.preventDefault();
if (!searchInput) {
return false;
}
try {
const response = await getGame(searchInput);
if (!response.ok) {
throw new Error('Something went wrong...');
}
const result = await response.json();
const gameData = result.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover,
}));
setSearchedGames(gameData);
setSearchInput('');
} catch (err) {
console.error(err);
}
};
Here are the API calls that go with those functions.
export const getGame = (searchInput) => {
return fetch(`https://id.twitch.tv/oauth2/token?client_id=************&client_secret=****************&grant_type=client_credentials`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
const accessToken = data.access_token;
return fetch(`https://fathomless-river-46653.herokuapp.com/https://api.igdb.com/v4/games/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Client-ID': '***********',
'Authorization': `Bearer ${accessToken}`
},
body: `
search "${searchInput}";
fields name,cover.url;`
})
});
};
export const getSpecificGame = (gameId) => {
return fetch(`https://id.twitch.tv/oauth2/token?client_id=************&client_secret=**************&grant_type=client_credentials`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
const accessToken = data.access_token;
return fetch(`https://fathomless-river-46653.herokuapp.com/https://api.igdb.com/v4/games/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Client-ID': '**************',
'Authorization': `Bearer ${accessToken}`
},
body: `
fields name,summary,platforms.name,genres.name,cover.url;
where id = ${gameId};`
})
});
}
This seems to be a simple matter of appropriately changing the syntax to function with useEffect instead of a form handler, but it is proving to be quite the challenge. I've looked over probably 20 threads of similar issues, but none of which have been able to solve my problem.
I've wondered if I should go another route entirely, completely removing the useEffect hook and trying something else, but this feels like a learning opportunity on how to make use of useEffect so I'm trying my best to get this working.
I would be happy to attach any extra code to assist with an answer. I posted a very similar question yesterday and received some helpful responses, but since then have made progress and reached another roadblock point. Thank you in advance to anyone who is able to assist!
const result = response.json();
There is a small error here, you need to await it:
const result = await response.json();
In the other example you posted from a form handler, it's awaited correctly, so it works.

Axios returning promise not destructed data in react with zustand

I'm currently trying to get axios running in a react hooks app. The basic get request is working fine and delivering the expected results, but here comes the problem:
I'm using 'zustand' for application state management and established a central backendService.js in which I have declared my async axios function 'getHome()'. The console.log(response.data); prints the expected data.
export const [useBackendStore, backendApi] = create((set, get) => ({
env: environments.local, //is declared above
home: null,
getHome: async () => {
return axios({ method: 'get', url: get().env + '/home/clean' })
.then(response => {
set({ home: response.data });
console.log(response.data);
return response.data;
})
.catch(error => {
console.log(error);
});
},
From another file I now want to call the getHome function and first of all log the results.
const data = backendApi.getState().getHome();
console.log(data);
And now the part I dont understand: The console.log gives me this
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Object
The PromiseResult has the expected object but i cant figure out how to access it.
I'm pretty new to backend development so please forgive me if I made any obvious mistakes:)
Thanks for your help!

My Network Response shows in the console but won't save to my variable

I am using React.js and Axios to call a django api on my local machine. When I make the request and check the network console, the data is there. But when I try to save the response data to a variable and log the results, nothing shows. I have tried using jquery and gotten the same results. It seems I can also only get the response data in the console when I use 'await'.
Portion of my Context-Provider.js code:
async componentDidMount() {
var resp = await axios.get('http://localhost:8000/campaign/')
.then((res) => {
resp = res.data
})
console.log(resp)
this.setState({
campaigns: resp.data,
})
}
Here is my consoles output for the network Response
Thinking about this more, it's probably a bad idea to combine async/await and then. I would suggest you go with one or the other
axios.get('http://localhost:8000/campaign/')
.then((res) => {
this.setState({ campaigns: res.data })
})
OR
const resp = await axios.get('http://localhost:8000/campaign/')
this.setState({ campaigns: resp.data })

Cannot fetch api due to array react native

I bulid an api using laravel which can run in postman (http://lkcfesnotification.000webhostapp.com/api/notifications). The problem is when i fetch using an example from this (https://www.youtube.com/watch?v=IuYo009yc8w&t=430s) where there is a array in the api then i have to setstate the array which is working well but when i try using the below code it does not render due to it is not using array in the api for example the random user api have "results" :[item], and mine one is "data":[my item]
fetchData = async () => {
const response = await fetch("https://randomuser.me/api?results=500");
const json = await response.json();
this.setState({ data: json.results });
};
if i use this will work but i want to use below code due to some homework i am doing
type Props = {};
export default class IndexScreen extends Component<Props> {
...
this.state = {
data: [],
isFetching: false,
};
_load() {
let url = "http://lkcfesnotification.000webhostapp.com/api/notifications";
this.setState({isFetching: true});
fetch(url)
.then((response) => {
if(!response.ok) {
Alert.alert('Error', response.status.toString());
throw Error('Error ' + response.status);
}
return response.json()
})
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
.catch((error) => {
console.log(error)
});
}
https://imgur.com/a/he5mNXv this is my render
the result i get the code i run is blank is loading
The fetch request is working but you are not saving the right data in the right state property.
The issues is located in the following part:
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
You are assigning the response to a variable members but saving another variable data, which does not exist.
In addition, the response is an object with more information than just the data, so what you are looking for is just the data property of the response.
This should work:
.then(({ data }) => {
this.setState({data});
this.setState({isFetching: false});
})
Here we destructure the response into the variable { data }, solving your issue.
Based on the snippets you don't use the fetched data to set it to your state:
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
membersis the result of your fetched json. So either rename members to data or use data: members. If the code should work like your first function it's probably data: members.result. You can also combine the two setState calls to one single call:
this.setState({
data: members.result,
isFetching: false,
});

ReactJS can not access response object inside setState

I am trying to update the setSet as part of output from my RestAPI. However I am getting an error that response object is undefined. I am able to log it outside setState method.
Code
addNewTodo = () => {
axios.post('http://localhost:5001/todos', "task="+this.state.newTodoList.task)
.then(response=>console.log(response.data))
.then(response=>{
this.setState(prevState=>({
TodoList: prevState.TodoList.push(response.data),
}))
});
{this.toggleNewTodoModal()}
}
I get following log in console before error
{task: "ddd", id: "todo10"}
Error:
TypeError: Cannot read property 'data' of undefined
at following line
TodoList: prevState.TodoList.push(response.data),
So your first .then returns a console log, meaning your second .then will no longer have any values. If you change your code to this:
Regarding pushing new Data to react state array, The recommended approach in later React versions is to use an updater function when modifying states to prevent race conditions. So pushing new Data to state array should be something like below
axios
.post('http://localhost:5001/todos', 'task=' + this.state.newTodoList.task)
.then(response => {
console.log(response.data);
this.setState(prevState => ({
TodoList: [...prevState.TodoList, response.data],
}));
});
It should work just fine. You can chain .then as much as you like, as long as you return some values, and not a console log, for example, in the fetch:
fetch('some_url', {
method: 'GET',
})
.then(res => res.json()) // this returns the data
.then(data => console.log(data)) // this has access to the data
My state object was a map, and so following worked for me.
State
state = {
TodoList: {},
}
Updating State
axios
.post('http://localhost:5001/todos', 'task=' + this.state.newTodoList.task)
.then(response => {
const {id, task} = response.data
this.setState(prevState => ({
TodoList: {...prevState.TodoList,
[id]: task},
}));
});

Resources