Cannot fetch api due to array react native - reactjs

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,
});

Related

How to send updated state in axios in React?

I am trying to send post request using axios in Reactjs.
I have two component a timer component and App component and in App component i am trying to submit a form and send an axios call when i fetch the time from Timer component and save itinto counter state
I have written a condition if counter is true then update my state and then further send the post request
Working Demo
here is a handle submit code:
const handleSubmit = e => {
console.log("handleSubmit");
e.preventDefault();
if (counter) {
console.log(counter);
const url = `url string`;
setState({
...state,
lastn: {
attestedTime: myDateFunc(),
time: counter
}
});
console.log(state);
axios
.post(url, state)
.then(response => {
console.log(response);
console.log(response.data);
})
.catch(error => {
console.log(error);
});
}
};
The problem is when counter is true its not update the state which causes error while send axios request.
I have consoled each and every thing but still it fails.
It seems there is lot of rendering.
If you are using class components, you can make the reuqest after the state has been set. Something like this:
this.setState({
...state,
lastn: {
attestedTime: myDateFunc(),
time: counter
}
}, () => {
axios
.post(url, state)
.then(response => {
console.log(response);
console.log(response.data);
})
.catch(error => {
console.log(error);
});
});
Since you did set the react-hooks tag, I guess that approach is not what you need. In your case, I suggest saving new state in some temporary variable and than passing that variable to axios. Like this:
const newState = {
...state,
lastn: {
attestedTime: myDateFunc(),
time: counter
}
};
setState(newState);
axios
.post(url, newState)
.then(response => {
console.log(response);
console.log(response.data);
})
.catch(error => {
console.log(error);
});
setState can be executed asynchronously by React, to optimize the rendering process. For cases like this one, it can also take a callback function that is guaranteed to be executed after updating the state.
For example:
this.setState({
name:'value'
},() => {
console.log(this.state.name);
});
in this case console.log will be executed after setting the name variable.
see the docs: https://reactjs.org/docs/react-component.html#setstate

React componentDidMount not setting states before page loads

Working on a MERN application, I have a componentDidMount that uses axios to retrieve from the backend some Ids and retrieve product info(prods) from the ids. However the states in my application are still empty when the page is loaded initially, instead I'll have to make a change to state before the states are set.
I believe it might have something to do with having an array mapping in the componenDidMount, I could change the backend so in node. However i would like to see if anything could be done in the frontend first.
componentDidMount() {
axios
.get("/api/featureds")
.then(response => {
this.setState({
featureIds: response.data
});
response.data.map(({ prodId, _id }) =>
axios
.get("/api/prods/" + prodId)
.then(response => {
if (response.data == null) {
} else {
this.state.featureTempList.push(response.data);
}
})
.catch(error => {
console.log(error);
})
);
this.setState({
featureProds: this.state.featureTempList
});
})
.catch(error => {
console.log(error);
});
}
Why are you trying to set state like this?
this.state.featureTempList.push(response.data)
State should be set by this.setState().
So you can try doing this:
this.setState((oldState) => ({
featureTempList: oldState.featureTempList.push(response.data)
});
Just remember to set featureTempList to state when you initialize:
state = {
featureTempList: []
}

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},
}));
});

SetState and React-Native lifecycle

I'm taking my first steps with React-Native. I can not understand why with the following code I get the value "data" = [] inside _refreshData (console.log(this.state.data);)
I have this code from Learning React Native book:
class SimpleList extends Component {
constructor(props) {
super(props);
console.log("Inside constructor");
this.state = { data: [] };
}
componentDidMount() {
console.log("Inside componentDidMount");
this._refreshData();
}
...
_refreshData = () => {
console.log("Inside_refreshData");
console.log(NYT.fetchBooks());
NYT.fetchBooks().then(books => {
this.setState({ data: this._addKeysToBooks(books) });
});
console.log("This is data: ");
console.log(this.state.data);
};
function fetchBooks(list_name = "hardcover-fiction") {
console.log("Inside fetchBooks");
let url = `${API_STEM}/${LIST_NAME}?response-format=json&api-
key=${API_KEY}`;
return fetch(url)
.then(response => response.json())
.then(responseJson => {
return responseJson.results.books;
})
.catch(error => {
console.error(error);
});
}
Debugging (with console.log) I see that "data" = [] even if I just called the setState and from the log I see that the fetch returned my values ...
This is the call log:
Can you explain why please?
Thanks in advance.
Ok, first it's promise and asynchronous, and it's not guaranteed that when you log your data also you receive the data, so when you are in componentDidMount and call console.log(this.state.data); maybe the data is not returned yet. think it took 2000 milliseconds to return the data from api. so you call
NYT.fetchBooks().then(books => {
this.setState({ data: this._addKeysToBooks(books) });
});
and then this code as I said took 2000 milliseconds, but as I said you immediately log the data so, because at this time data is not filled you see the empty array.but if you want to see the data you can log it here :
NYT.fetchBooks().then(books => {
console.log(books);
this.setState({ data: this._addKeysToBooks(books) });
});

React - wait for promise before render

I can't figure this out, if you can point me to right direction. On my NavMenu.js I have LogIn (two inputs and a submit button, on which I call handleSubmit()
In handleSubmit() I am checking for user login credentials which works great, but after I confirm login, i procede with doing next fetch for checking user roles (and returning promise) and visibility in application
Helper.js
function getAllRoles(formName, sessionVarName) {
var roleData = [];
var roleId = sessionStorage.getItem('UserLoggedRoleId');
fetch('api/User/GetUserRole/', {
'method': 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
'body':
JSON.stringify({
'roleId': roleId,
'formName': formName
})
}).then(roleResponse => roleResponse.json())
.then(data => {
sessionStorage.setItem(sessionVarName, JSON.stringify(data));
roleData = data;
});
var result = Promise.resolve(roleData)
return result;
}
function checkRoleVisibility(userRoles, role) {} <-- return true or false
export {
getAllRoles ,
checkRoleVisibility
};
In NavMenu.js
import { getAllRoles, checkRoleVisibility } from './common/Helper';
handleSubmit() {
fetch('api/User/UserAuthentification/',
....then(response => {
if (response.ok) {
this.setState(prevState => ({ userLogged: !prevState.userLogged }))
response.json().then(value => {
sessionStorage.setItem('UserLogged', true);
sessionStorage.setItem('UserLabelSession', this.state.userLabel);
sessionStorage.setItem('UserLoggedRoleId', value.userRole.roleID);
getAllRoles('NavMenu', 'UserLoggedRoles')
.then(roleData => {
this.setState({
loggedUserRoles: roleData,
loading: false
});
});
this.props.alert.success("Dobro dosli");
})
}
}
But here comes the problem, is that in render() itself, I have a control navBar which calls
checkRoleVisibility(this.state.loggedUserRoles, 'SeeTabRadniNalozi')
from helper, which send this.state.loggedUserRoles as parameter, which suppose to be filled in fetch from handleSubmit() but it's undefined, fetch finish, set loading on true, rendering completes and it doesn't show anything
I have this.state.loading and based on it I show control ...
let navMenu = this.state.loading ? <div className="loaderPosition">
<Loader type="Watch" color="#1082F3" height="200" width="200" />
</div> : !this.state.userLogged ? logInControl : navBar;
If I put a breakpoint on controler I can see my method is being called by getAllRoles function, but also I can see that application keep rendering on going and doesn't wait for promise to return to fill state of loggedUserRoles.
So question is, why rendering doesn't wait for my role fetch nor doesn't wait for promise to resolve before setting loading on true?
I checked this answer Wait for react-promise to resolve before render and I put this.setState({ loading: false }); on componentDidMount() but then loading div is shown full time
From what I see and understand is, that you're calling
this.setState(prevState => ({ userLogged: !prevState.userLogged }))
if the response is okay. This will lead in an asynchronous call from React to set the new State whenever it has time for it.
But you need the resource from
this.setState({
loggedUserRoles: roleData,
loading: false
});
whenever you want to set userLogged to true, right?
So it might be that React calls render before you set the loggedUserRoles but after userLogged is set to true. That would result in an error if I understand you correctly.
You could simply set the userLogged state with the others together like this:
this.setState({
userLogged: true,
loggedUserRoles: roleData,
loading: false
});
EDIT:
Moreover you want to change your Promise creation inside of getAllRoles. You're returning
var result = Promise.resolve(roleData)
return result;
This will directly lead to the empty array since this is the initial value of roleData. Therefore you're always returning an empty array from that method and don't bother about the fetched result.
You've multiple ways to solve this. I would prefer the clean and readable way of using async/await:
async getAllRoles(formName, sessionVarName){
var roleId = sessionStorage.getItem('UserLoggedRoleId');
const response = await fetch(...);
const data = response.json();
sessionStorage.setItem(sessionVarName, JSON.stringify(data));
return data;
Or if you want to stay with Promise:
function getAllRoles(formName, sessionVarName){
return new Promise((resolve, reject) => {
var roleId = sessionStorage.getItem('UserLoggedRoleId');
fetch(...)
.then(roleResponse => roleResponse.json())
.then(data => {
sessionStorage.setItem(sessionVarName, JSON.stringify(data));
resolve(data);
});

Resources