Learning JavaScript and React Native and I seem to not be understanding how to put a json response into a variable I can access. I have looked at this, this, this and also the Mozilla documentation and also this and a lot more but still don't seem to grasp the concept or get it to work.
export default class AwesomeApp extends Component {
constructor(props) {
super(props);
this.state = { questions: [] };
}
componentWillMount() {
this.getQuestionsFromAPI().then((res) => {
this.setState({
questions: res
});
});
let url = 'http://127.0.0.1:3000/trivia';
async function getQuestionsFromAPI() {
fetch(url).then(response => response.json())
.then(function(json) {
questions = json;
//console.log(questions[0]);
return questions;})
.catch(error => console.log(error));
}
}
render() {
return (
<View style={styles.container}>
<Text style={styles.question}>
{ this.props.questions[0] }
</Text>
<View style={{ flex: 1, padding: 20 }}>
<CheckBox
label={ this.props.questions }
checked={false}
onChange={(checked) => console.log('I am checked', checked)}
/>
</View>
AppRegistry.registerComponent('AwesomeApp', () => AwesomeApp);
I get the error "undefined is not a function (evaluating 'this.getQuestionsFromAPI()'). Looking at it in a browser, setting:
var quests = getQuestionsFromAPI()
returns a promise
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}
while
console.log(questions[0]);
returns an object which is what I want. What am I not understanding?
There are a few issues in the code sample you sent above.
Firstly, if you indent your code properly you will notice that the function getQuestionsFromAPI is declared within componentWillMount. Meaning that when you refer to it using this it is not found.
Secondly, getQuestionsFromAPI does not return a promise. You should return fetch(...).
Finally, you are trying to get the questions using this.props.questions, but you are assigning the questions in the state, so you should be using this.state.questions instead.
Related
I am trying to add translation system in my app. My sample code:
async translate(txt){
var res = await translate(txt, {
//some props
})
return res[0] //it was returned as an object, so taking the first element
}
render(){
return (
<View>
<Text>{this.translate('Hello')}</Text>
</View>
)
}
I know, async returns a promise but I could not figure out a way to use it in my case.
I have checked other questions, but those didn't work :(
You would need to wait for the promise to resolve before displaying the output of translate so you could use a state to store the output once translate finishes.
constructor() {
super();
this.state = {};
}
componentDidMount() {
this.translate("Hello").then((output) => this.setState({ text: output }));
}
render() {
return (
<View>
<Text>{this.state.text ? this.state.text : "Loading"}</Text>
</View>
);
}
im new in react-native, im passing data by navigation to my edit_note screen, once i received i set it to my states, but it doesnt work, if i print them, it shows their values, but setting to my states doesnt work, heres the code:
heres the Notes class, in the navigation function im passing the datas, data and note_number to Edit_note
render() {
return (
<>
<View style = {this.styles.View}>
<FlatList data = {this.props.data} renderItem = {({item}) => (<TouchableOpacity onPress = {() => this.props.navigation.navigate("Edit_note", {data: this.props.data, note_number: item.note_number})}><Text style = {this.styles.Text}>{item.title}</Text></TouchableOpacity>)} keyExtractor = {(item) => item.note_number.toString()}></FlatList>
</View>
</>
);
}
in Edit_note im receiving it like this:
class Edit_note extends Component {
constructor() {
super();
this.state = {
array_notes: [],
note_number: "",
}
}
componentDidMount() {
const {params} = this.props.navigation.state;
let x = params.note_number;
this.setState({note_number: x});
console.log(this.state.note_number);
}
render() {
return (
<Text></Text>
);
}
}
if i print x, it will print the note_number, but setting it into note_number, and print it, it doesnt show anything, why?
You actually set it but your console.log() fires before the change.
After a state changes component rerenders so you can try printing it on screen like;
render() {
return (
<Text>{this.state.note_number}</Text>
);
}
setState is asynchronous that means that the state indeed changes but the console.log right after the setState is not showing the change.
You can learn more about it here.
I am making an app using React Native and store information about users in a collection called "User" on Firestore. Each user has a profile picture url stored in the collection, and I want to display multiple user images on the same page. I am struggling, however, to get this to work due to having to wait for Promises to return.
I have tried storing the url in a state variable when it is retrieved, however, this would involve creating loads of state variables due to the number of images I want to display. I then tried using async/await and then statements but the images won't load due to the promises not returning in time.
async getImg(user_id) {
return await firebase.firestore().collection('User').doc(user_id).get()
.then(user => {return user.data().image})
render() {
<SafeAreaView style={styles.container}>
<Image source={{uri: this.getImg('rwWa39Y6xtS1nZguswugODWndqx2') }} style={{ ... }} />
<Image source={{uri: this.getImg('HQkCoChUe5fkZrHypYdRnrw66Rp2') }} style={{ ... }} />
</SafeAreaView>
);
}
The above code is my latest attempt and it returns the following error due to a promise being returned instead of the string url.
You attempted to set the key `_65` with the value `1` on an object that is meant to be immutable and has been frozen.
Does anyone have any idea of how to solve this?
You are mixing the use of async/await with the then() method.
By doing as follows:
async getImg(user_id) {
const userSnapshot = await firebase.firestore().collection('User').doc(user_id).get()
return userSnapshot.data().image;
}
you will declare an asynchrnous getImg() function.
I don't know react-native so I don't know if it would work by using it in
<Image source={{uri: this.getImg('rwWa39Y6xtS1nZguswugODWndqx2') }} style={{ ... }} />
But #VolkanSahin45 solution, adapted as follows, should work:
async getImg(user_id) {
const userSnapshot = await firebase.firestore().collection('User').doc(user_id).get()
this.setState({
img: userSnapshot.data().image;
})
}
Note that it would be good to handle errors with try/catch as follows:
async getImg(user_id) {
try {
const userSnapshot = await firebase.firestore().collection('User').doc(user_id).get()
this.setState({
img: userSnapshot.data().image;
})
} catch (error) {
this.setState({
img: 'default_user_img.png';
})
}
}
getImg function returns Promise. Instead you can save img to state and render if there is img.
async getImg(user_id) {
return await firebase.firestore().collection('User').doc(user_id).get()
.then(user => {
this.setState({
img: user.data().image
})
}
)
}
render() {
const { img } = this.state;
return(
<SafeAreaView style={styles.container}>
img && <Image source={{ img }} style={{ ... }} />
</SafeAreaView>
)
}
I'm attempting to set a global variable equal to a JSON element being returned from a Promise with Axios, within my React Native application. I've followed the advice from this question, but still am unable to set the variable's value.
Here is my method using an Axios call:
temp_high = null;
_getForecast(zipcode)
{
const request = "http://api.wunderground.com/api/" + API_KEY + "/forecast/q/" + zipcode + ".json";
return axios.get(request).then( (response) => {
if(response.status == 200) {
this.response = response.data;
return this.response;
}
});
}
And my render:
render() {
this._getForecast(49306).then(data => {
this.temp_high = parseInt(data.forecast.simpleforecast.forecastday[0].high.fahrenheit);
});
return (
<View style={styles.container}>
<Text>Weather for Belmont, MI</Text>
<Text>High: {this.temp_high}</Text>
<Text></Text>
</View>
);
}
}
If I log data.forecast.simpleforecast.forecastday[0].high.fahrenheit to the console, or create an alert calling that, the value is indeed correct. I just cannot seem to set it equal to temp_high.
If you want your component's view to update in response to the new data, you need to use setState, to tell React to re-render. React doesn't react (hue) to regular class properties.
Async functions shouldn't be called from render. You should instead use lifecycle hooks, and componentDidMount would work best for this situation, to fetch the information once on mount.
With that in mind, you'd end up with something like this:
class Example extends React.Component {
state = {
data: null
}
componentDidMount() {
// fetch forecast data when the component mounts
this._getForecast(49306)
}
_getForecast(zipcode) {
const request =
"http://api.wunderground.com/api/" +
API_KEY +
"/forecast/q/" +
zipcode +
".json"
return axios.get(request).then(response => {
if (response.status == 200) {
this.setState({ data: response.data })
}
})
}
render() {
if (this.state.data === null) {
// don't render if we haven't received data yet
// otherwise we'll get an error trying to calculate temp_high
return
}
const temp_high = Number(
this.state.data.forecast.simpleforecast.forecastday[0].high.fahrenheit
)
return (
<View style={styles.container}>
<Text>Weather for Belmont, MI</Text>
<Text>High: {temp_high}</Text>
<Text />
</View>
)
}
}
If you want to assign to a standalone variable (even if it happens to be in the global scope), don't prefix it with this. Just
temp_high = Number(data.forecast.simpleforecast.forecastday[0].high.fahrenheit);
this resolves to the calling context of the current function you're in.
This answer is wrong, the arrow allows for https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) be carefull with the keyword "this".
It is currently setting the this.temp_high to the _getForecast function. What you might want to do is have
render() {
var self = this;
this._getForecast(49306).then(data => {
self.temp_high
=parseInt(data.forecast.simpleforecast.forecastday[0].high.fahrenheit);
});
return (
<View style={styles.container}>
<Text>Weather for Belmont, MI</Text>
<Text>High: {this.temp_high}</Text>
<Text></Text>
</View>
);
}
}
Hey everyone so I am making a get request to a google API and pulling in some data. Initially, my state value is just an empty object and from the ajax request I am expecting the state values to be filled with setState.
Then, in another method I am taking that state data and mapping over the items and returning an option for each element within that array. The weird thing is that right before I start returning the values that I am mapping over I am console.loging the values and they are exactly the values that I want. However, when I return an option with that value inside there is still nothing inside my select.
Can anyone please elaborate what I could be possibly doing incorrectly?
constructor() {
super();
this.state = {
};
componentDidMount() {
let URL = is the url with my api key (it works)
axios.get(URL)
.then((data) => {
console.log("data" + data);
this.setState({
googleFonts: data
})
})
.catch((err) => {
console.log(err);
this.setState({
errors: err
})
})
}
renderFonts() {
let data = this.state.googleFonts.data.items;
return data.map((font, index) => {
console.log(font.family);
return (
<ul>
<li>{font.family}</li>
</ul>
)
})
}
<FormControl
style={inputFieldStyle}
componentClass="select"
placeholder="select" >
{setTimeout(this.renderFonts, 100)}
</FormControl>
Could you try something like this:
//after `render()`
const data = this.state.googleFonts.data.items
return (
<FormControl
style={inputFieldStyle}
componentClass="select"
placeholder="select"
>
{data && data.map((font, index) =>
<option>{font.family}</option>
)}
</FormControl>
I believe that's how React-Bootstrap is expecting it to be written, and it's a little less code. Using the logical && operator allows it to populate the dropdown after the state loads.
There are a few things wrong here.
First: {setTimeout(this.renderFonts, 100)} returns timeoutID is a positive integer value which identifies the timer created by the call to setTimeout() and hence although renderFonts is executed nothing is returned from it to be rendered
Second: componentDidMount is called after render and you have your API request in componentDidMount, so delaying the value to be rendered by using a setTimeout is a horrible idea, since you are never sure as to when you would get a response from the API, for a slow network it could take a really long time and hence even after a timeout of 100ms, you may still not have the data and so this.state.googleFonts.data.items might still be undefined or the response may come within 10ms in which case you are unnecessarily delaying the render.
A better way to solve this is to have provide a check for the presence of data.
You could do it like
constructor() {
super();
this.state = {
};
componentDidMount() {
let URL = is the url with my api key (it works)
axios.get(URL)
.then((data) => {
console.log("data" + data);
this.setState({
googleFonts: data
})
})
.catch((err) => {
console.log(err);
this.setState({
errors: err
})
})
}
renderFonts() {
let data = this.state.googleFonts? this.state.googleFonts.data.items: [];
return data.map((font, index) => {
console.log(font.family);
return (
<ul>
<li>{font.family}</li>
</ul>
)
})
}
<FormControl
style={inputFieldStyle}
componentClass="select"
placeholder="select" >
{this.renderFonts()}
</FormControl>
This should help you. It's more to do with how you have structured your code and callback. Take a look at this sample -
import React from 'react';
import axios from 'axios'
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'there',
title: null
};
}
componentWillMount() {
let URL = 'https://fakerestapi.azurewebsites.net/api/Books'
axios({
method: 'get',
url: URL,
responseType: 'json'
})
.then((data) => {
console.log(data.data);
this.setState({
title: data.data[0].Title
})
})
.catch((err) => {
console.log(err);
this.setState({
errors: err
})
})
}
render() {
return (
<div className="component-app">
<h1>Hello {this.state.name}! {'\u2728'}</h1>
<h2>{this.state.title}</h2>
</div>
);
}
}
export default Hello;
I have coded it here. Have a look
Instead of componentDidMount you can use componentWillMount. Also you would be seeing errors in console with your code somthing like React - setState() on unmounted component. If you fix that you should be able to get your code working.