Getting undefined when json fetch in react native - json

In my simplified code below I am able to pull data in JSON format but if I try to console log a specific key of the object, I get undefined.
I had thought I could use this.state.profile.name to display "Steven".
Why is this coming up as undefined when I'm able to see the entire response? Thanks!
state = {
responseJSON: null,
};
callGraph = async token => {
const response = await fetch(
`https://graph.facebook.com/me?access_token=${token}&fields=id,name,email,about,picture`
);
const responseJSON = JSON.stringify(await response.json());
this.setState({
profile: responseJSON
});
console.log("profile = " + this.state.profile);
};
this console.log output the following:
profile = {"id":"*******","name":"Steven *******","email":"steve#*******.com","picture":{"data":{"height":50,"is_silhouette":false,"url":"https://platform-lookaside.fbsbx.com/platform/profilepic/?asid=*******&height=50&width=50&ext=1539943832&hash=AeQM2VfUBmdfOVJZ","width":50}}}

setState is asynchronous.
In your code, you are trying to console.log before the state is updated.
You need to use the callback as the second argument of the setState function:
this.setState({
profile: responseJSON
}, () => {
console.log("profile = " + this.state.profile);
});
Check this post for more information or also this other question

setState is asynchronous. The answer of Kevin is completely right. However, you can also use this method to see the result immediately:
this.profile= responseJSON;
console.log("profile = " + this.profile);
You should also define it in the constructor like this:
constructor(props) {
super(props);
this.profile='';
}
Also, in order to access the parameter of profile in your screen you need to use this.profile.

Related

react useState setState is undefined on firstload but works perfectly after refresh

I am learning react and making a weather app. It is fetching API from open weather and assigns response to data and then setting value to city using setCity. But on first load the city varaible is undefined and when I console log out data it has all the JSON object.
const [city, setCity] = useState({})
useEffect(()=>{
getWeather()
},[setCity])
const getWeather = async ()=> {
const reqAPI = `http://api.openweathermap.org/data/2.5/weather?q=toronto&units=metric&appid=${API_KEY}`
const response = (await fetch(reqAPI)).json()
const data = await response
console.log(data)
setCity(data)
console.log(city)
}
console log data is ok giving all value but city is undefined on first load and crashes the app. Please help as I am not able to find a solution
Here is the console output both should be same but are not!
You can see after editing out the code I am still getting this error
line 20 and 23 console output
Fetch gives promise as output which needs to be handled with .then() i.e then() part is executed once promise once the promise is complete. Also add your state city to dependency array as stated below.
Update your useEffect like this
useEffect(() => {
const reqAPI = `http://api.openweathermap.org/data/2.5/weather?q=toronto&units=metric&appid=${API_KEY}`;
fetch(reqAPI)
.then((response) => {
console.log(response);
response = response.json();
console.log(response);
setCity(response);
})
.catch((e) => console.log(e));
}, [city]);
and drop getWeather function.
Have a look at promises here
Update:
Render your city component only if the city is not an empty object like this
{Object.keys(city).length !== 0 ? <YourCityComponent/> : <div></div>}
Here, i have added a check to not render when city is {} and in the above statement <YourCityComponent/> refers to your <City key={city.id} name={city.name} etc./>.
It can be done in multiple ways but this is the easiest on to understand.
Have a look at this too on how to check if your object is empty.
City being an empty object on the first load is exactly what is supposed to happen. Don't fix it, instead.. handle this state.
First, it's better to initialize the state with undefined:
const [city, setCity] = useState()
Then later in the component, handle this case:
return <div>Loading...</div>;
You should move your getWeather function to inside the useEffect hook. Also react state updates are asynchronous so when you're trying to console.log(city.main) right after you setCity there is no guarantee that the state has been updated. If you want to console.log when the state has been updated then you could use another useEffect with city in the dependency array.

fetching data with react hook returns undefined on nested obj properties

Im trying to display data that has been fetched. but i cannot seem to display nested objects properties in react. Any ideas? if i log the data i first get a undefined, then the correct data.
my guess is that i need to wait for the data to be loaded then display it. but it does work for the title that is not in a nested obj.
function SingleBeneficiary({ match }) {
const [data, setData] = useState({ data: []});
const id = match.params.id
useEffect(() => {
async function fetchData() {
const response = await fetch(`http://localhost:8081/v1/beneficiary/${id}`);
const jsonData = await response.json()
setData(jsonData)
}
fetchData();
}, [])
return (
{data.title} // works
{data.address.careOf} // dont work
The data
{
"title":"myTitle",
"address":{
"careOf": "my adress"
}
}
Can you try like this?
I set initial data to null, and in return I check if it is not null.
If address can be null, additional null check is required.
function SingleBeneficiary({ match }) {
const [data, setData] = useState(null);
const id = match.params.id
useEffect(() => {
async function fetchData() {
const response = await fetch(`http://localhost:8081/v1/beneficiary/${id}`);
const jsonData = await response.json()
setData(jsonData)
}
fetchData();
}, [])
return (
<div>
{data && (
<div>
<p>data.title</p>
<p>data.address.careOf</p>
</div>
)}
</div>
);
}
You should check if address has careOf property before using it because first time data will be undefined and in second render it will have the data after the api call.
{data.address && data.address.careOf}
For anyone who is having a similar issue(i.e. fetching data via api and only the first time it runs, it will show the data as undefined but after manual refreshing, it works fine), here is a quick and sketchy addition you might consider alongside with 1. "Inline If with Logical && Operator" method and 2. using useState for checking if the api loading is over. With those three, mine worked.
Try fetching the desired data in the previous page of your app; in this case, add the following lines in any page you'll see before "SingleBeneficiary".
const response = await fetch(`http://localhost:8081/v1/beneficiary/${id}`);
const jsonData = await response.json()
Maybe it has to do with npm cache, but not really sure what's going on.
replace
return (
{data.title}
{data.address.careOf}
)
with
return (
{data?.title}
{data?.address?.careOf}
)

I'm getting a 404 error when trying to render my results for my API Hack assignment

I'm working on an API Hack assignment for my class with Thinkful and my issue has been that I've been trying to make a call to spoonacular's food api and render the results onto the DOM. However, when I try to do that, All I get in return is a 404 error. I'm wondering if i did something wrong or is some unforeseen problem that is beyond my control?
I've already look at manually typing the composed URL and postman as well.
function queryParams(params) {
const queryItems = Object.keys(params).map(key => `${encodeURIComponent(key)}= ${encodeURIComponent(params[key])}`)
return queryItems.join('&');
}
function displayResults(responseJson){
console.log(responseJson);
$('#results-list').empty();
for(let i = 0; i < responseJson.results.length; i++){
$('#results-list').append(
`<li><h3>${responseJson.results[i].id},${responseJson.results[i].protein}</h3>
<p>By ${responseJson.results[i].calories}</p>
<img src='${responseJson.results[i].image}'>
</li>`
)};
$('#results').removeClass('hidden');
};
function getRecipe(query,maxResults,){
const params ={
q:query,
number: maxResults,
};
const queryString = queryParams(params)
const url = searchUrl+'?'+ queryString +'?apiKey='+ apikey;
console.log(url);
fetch(url,option)
.then(response =>{
if(response.ok){
return response.json();
}
throw new Error(response.statusText);
})
.then(response => console.log(responseJson))
.catch(err =>{
$('#js-error-message').text(`Something went wrong: ${err.message}`);
});
}
function watchForm() {
$('form').submit(event => {
event.preventDefault();
const searchRecipe = $('.js-search-recipe').val();
const maxResults = $('.js-max-results').val();
getRecipe(searchRecipe, maxResults);
});
}
$(watchForm);
It looks like you have a couple issues:
First, you're constructing an invalid url:
const url = searchUrl+'?'+ queryString +'?apiKey='+ apikey;
notice the 2 ?s
Also, when you're constructing the query params, you're adding a space between the = and the value of your param
${encodeURIComponent(key)}= ${encodeURIComponent(params[key])}
If you're using the correct path and a valid API key, fixing those things may be enough to make it work.

How to set a route before a component is created in unit test

I have a component which when created extracts a parameter from url and launches a query.
ngOnInit() {
....
this.id = this.route.snapshot.paramMap.get("my-id");
console.log("will query db for question with id " + this.id);
this.myService.getId(this.id)
}
I want to unit-test the component but I can't figure out how to set the url before TestBed creates this component.
The url should be of format
{
path:'id-details;my-id=:id',// for eg id-details;my-id=1
component:MyComponent
},
I thought I could set the route in beforeEach but that isn't working
beforeEach((done) => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
let router = TestBed.get(Router);
let location = TestBed.get(Location);
router.navigateByUrl("/id-details;my-id=1");
});
fit('should create', () => {
expect(component).toBeTruthy();
});
In above spec, the component gets created but I don't get id. I tried extracting params in the followinng two ways but I always get null
this.id = this.route.snapshot.paramMap.get("my-id"); //get null
and
this.id = this.route.snapshot.paramMap.get("id-details"); //get null
I also see the following error in the console
context.js:1972 Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?
Unhandled Promise rejection: Cannot match any routes. URL Segment: 'question-details;question-id=1' ; Zone: ProxyZone ; Task: Promise.then ; Value: Error: Cannot match any routes. URL Segment: 'question-details;question-id=1'
Try this:
spyOn(component.route.snapshot.paramMap,"get").and.returnValue("some_id")
Or:
let fake_value = "value";
spyOn(component.route.snapshot.paramMap,"get").and.callFake(()=>{
return fake_value;
});
fake_value = "another";
component.someMethod();
expect(...).toBe("another");
fake_value = "then_";
component.someMethod();
expect(...).toBe("then_");

How to efficiently fetch data from URL and read it with reactjs?

I have some URL with json and need to read data.
For the sake of this example json looks like this:
{
"results": [
...
],
"info": {
...
}
}
I want to return fetched data as a property of a component.
What is the best way to do it?
I tried to do that with axios. I managed to fetch data, but after setState in render() method I received an empty object. This is the code:
export default class MainPage extends React.Component {
constructor(props: any) {
super(props);
this.state = {
list: {},
};
}
public componentWillMount() {
axios.get(someURL)
.then( (response) => {
this.setState({list: response.data});
})
.catch( (error) => {
console.log("FAILED", error);
});
}
public render(): JSX.Element {
const {list}: any = this.state;
const data: IScheduler = list;
console.log(data); // empty state object
return (
<div className="main-page-container">
<MyTable data={data}/> // cannot return data
</div>
);
}
}
I don't have a clue why in render() method the data has gone. If I put
console.log(response.data);
in .then section, I get the data with status 200.
So I ask now if there is the other way to do that.
I would be grateful for any help.
----Updated----
In MyTable component I got an error after this:
const flightIndex: number
= data.results.findIndex((f) => f.name === result);
Error is:
Uncaught TypeError: Cannot read property 'findIndex' of undefined
What's wrong here? How to tell react this is not a property?
Before the request is returned, React will try to render your component. Then once the request is completed and the data is returned, react will re-render your component following the setState call.
The problem is that your code does not account for an empty/undefined data object. Just add a check, i.e.
if (data && data.results) {
data.results.findIndex(...);
} else {
// display some loading message
}
In React, after you have stored your ajax result in the state of the component (which you do appear to be doing), you can retrieve that result by calling this.state.list
So to make sure this is working properly, try <MyTable data={this.state.list}>
https://daveceddia.com/ajax-requests-in-react/