(ReactAssign firebase snapshot to a variable outside of the function - javascript

Hi i am trying to get the data from my database, in componentDidMount(), it works fine with this :
componentDidMount() {
firebase.database().ref('/users/' + user.uid).once('value').then(function(snapshot) {
var valeur = snapshot.val();
return valeur;
});
this.setState({userData:valeur});
}
I just want to know how can i assign this data to a variable that can be use outside of this function, i tried setstate and using variable that i declare outside nothing work i get :react native possible unhandled promise rejection (id 0) Typeerror: this.setState is not a function. Thank you

class Search extends React.Component {
constructor(props) {
super(props)
this.state = {
films: [],
isLoading: false,
userData: null
}
this.searchedText = ""
this.localData=null;
}
componentWillMount() {
user = firebase.auth().currentUser;
firebase.database().ref('/users/' + user.uid).once('value').then(snapshot => {
pro = snapshot.val()
this.setState({userData: pro});
});
this.setState({ isLoading: true })
getFilmsFromApiWithSearchedText().then(data => this.setState({ films: data.results }))
this.setState({
films: data.results,
isLoading: false
})
console.log(this.state.userData)
}
_displayLoading() {
if (this.state.isLoading) {
return (
<View style={styles.loading_container}>
<ActivityIndicator size='large' />
</View>
)
}
}
_signOut() {
firebase.auth().signOut();
}
_displayDetailForFilm = (idFilm) => {
this.props.navigation.navigate("FilmDetail", { idFilm: idFilm })
}
render() {
return (
<View style={styles.main_container}>
<StatusBar
hidden = {false}
backgroundColor="#F1D145"
></StatusBar>
<TouchableOpacity onPress={() => this._signOut()} style={styles.button} activeOpacity={0.8}>
<Text style={styles.button_text}>Déconnexion</Text>
</TouchableOpacity>
<Text>{user.uid}</Text>
<FlatList
data={this.state.films}
keyExtractor={(item) => item.id.toString()}
renderItem={({item}) => <FilmItem film={item} displayDetailForFilm={this._displayDetailForFilm}/>}
/>
{this._displayLoading()}
</View>
)
}
}
this.setState({ isLoading: true })
getFilmsFromApiWithSearchedText().then(data => this.setState({ films: data.results }))
this.setState({
films: data.results,
isLoading: false
})
console.log(this.state.userData)
}
Here is all the code i already do console.log(this.state.userData) it return null

You have to setState inside the .then function.
Then you can access your value with this.state.userData.
It will be undefined just after mounting then defined when your fetch resolves, so integrate a mechanism that checks for that.
For your undefined problem, a simple solution would be that for instance:
render() {
return (
<View>{this.state.userData && this.state.userData.photo}</View>
)
}
Edit: also like Kai pointed out I am not sure you have access to this instance inside .then callback if you don't use arrow notation.

Switch the anonymous function that you're passing to .then to an arrow function to allow you to use the this from the class scope.
Plenty of information elsewhere on this

Related

TypeError: Cannot read properties of undefined (reading 'id') in React

I have built a website but I encounter a problem when I press button "Load More" after 3 times. It gives me an error of "TypeError: Cannot read properties of undefined (reading 'id')"...I guess something wrong with slice and map function!
P.S. I tried to call "id" to other component but in vain!
class PostsList extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: [],
visible: 3,
error: false
};
this.loadMore = this.loadMore.bind(this);
}
loadMore() {
this.setState((prev) => {
return {visible: prev.visible + 3};
});
}
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/posts").then(
res => res.json()
).then(res => {
this.setState({
posts: res
});
}).catch(error => {
console.error(error);
this.setState({
error: true
});
});
}
render() {
const { classes } = this.props;
return (
<div className={classes.section}>
<h2 className={classes.title}>Latest news</h2>
<div>
<GridContainer>
{
this.state.posts.slice(0, this.state.visible).map((post, i) => {
return (
<PostCard key={i}
id={posts[i].id}
date={posts[i].date}
fbimg={posts[i].fbimg}
description={posts[i].description}
fbpost={posts[i].fbpost}
/>
);
})
}
</GridContainer>
<GridContainer>
<GridItem>
{this.state.visible < this.state.posts.length &&
<Button
color="info"
onClick={this.loadMore}
>
Load more
</Button>
}
</GridItem>
</GridContainer>
</div>
</div>
);
}
}
Thank you in advance!!!
posts is not defined inside your render function. Did you mean this.state.posts?
Also, there's no need to use the index to access posts when you already have the single post available from your map function. So change posts[i].id to post.id.

Problem getting value of text input in react native

I've tried to console.log the value of the text input but I get the error "undefined is not an object (evaluating 'this.state.inputValue')". What's the problem? Thank you!
class SearchScreen extends React.Component {
state = {
inputValue: "",
};
search() {
console.log(this.state.inputValue);
}
render() {
return (
<View>
<TextInput
onChangeText={
((inputValue) => this.setState({ inputValue }),
this.search)
}
value={this.state.inputValue}
/>
</View>
);
}
}
export default SearchScreen;
The problem is in the way you've implemented it. Please try as below...
class SearchScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
};
}
search() {
console.log(this.state.inputValue);
}
render() {
return (
<View>
<TextInput
onChangeText={(inputValue) => {
this.setState({ inputValue });
this.search();
}}
value={this.state.inputValue}
/>
</View>
);
}
}
export default SearchScreen;
This problem occurred because two things.
First:
The this.setState is a async function.
If you pass a function after the setState this will work like a .then() in a promisse.
Second:
If you pass one function after another separating them by ',' the rightmost function will be executed first
You can resolve this doing something like that:
onChange={ inputValue => {
this.setState({ inputValue });
this.search();
}}
Or you can try something like that:
class SearchScreen extends React.Component {
state = {
inputValue: "",
};
search = () {
console.log(this.state.inputValue);
}
setSearch = inputValue => {
// The function 'search' will be execute after the state was set
this.setState(
{ inputValue },
() => this.search()
);
}
render() {
return (
<View>
<TextInput
onChangeText={ inputValue => this.setSearch(inputValue) }
value={this.state.inputValue}
/>
</View>
);
}
}
export default SearchScreen;
You didn't set the value of state property. provide a value to setState.
this.setState({property : value})
The problem is this line:
onChangeText={
((inputValue) => this.setState({ inputValue }),
this.search)
}
You can use the short function notation only when your function has one statement:
(inputValue) => this.setState({ inputValue })
You actualy have 2 statements though, so you need to create a full function block using {}
(inputValue) => {
this.setState({ inputValue })
this.search()
}

How to get AsyncStorage data without waiting in react native?

I have trouble trying to retrieve data from AsyncStorage, I can't directly assign a state like that, since it always returns undifined, how can I avoid that?
export default class ListTodo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {},
};
}
componentDidMount() {
//promise
GetDataAsyncStorage('#TODOS').then((data) => {
this.setState({
data: data,
});
});
}
render() {
const {data} = this.state;
console.log(data); // undifined
return (
<>
<Header />
<View>
<FlatList
data={data}
renderItem={({item}) => <TodoItemComponent data={item} />}
keyExtractor={(item) => item.id}
/>
</View>
</>
);
}
}
Here is my function to get data from asynStorage
export const GetDataAsyncStorage = async (key) => {
try {
let data = await AsyncStorage.getItem(key);
return {status: true, data: JSON.parse(data)};
} catch (error) {
return {status: false};
}
};
Add a state variable isLoading and toggle it after the data is got from AsyncStorage
snack: https://snack.expo.io/#ashwith00/async
code:
export default class ListTodo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {},
isLoading: false,
};
}
componentDidMount() {
this.getData();
}
getData = () => {
this.setState({
isLoading: true,
});
//promise
GetDataAsyncStorage('#TODOS').then((data) => {
this.setState({
data: data,
isLoading: false,
});
});
};
render() {
const { data, isLoading } = this.state;
return (
<View style={styles.container}>
{isLoading ? (
<ActivityIndicator />
) : data.data ? (
<FlatList
data={data}
renderItem={({ item }) => <Text>{item}</Text>}
keyExtractor={(item, i) => i.toString()}
/>
) : (
<Text>No Data Available</Text>
)}
</View>
);
}
}
Because AsyncStorage itself is asynchronous read and write, waiting is almost necessary, of course, another way to achieve, for example, to create a memory object, bind the memory object and AsyncStorage, so that you can read AsyncStorage synchronously.
For example, using the following development library can assist you to easily achieve synchronous reading of AsyncStorage react-native-easy-app
import { XStorage } from 'react-native-easy-app';
import { AsyncStorage } from 'react-native';
// or import AsyncStorage from '#react-native-community/async-storage';
export const RNStorage = {
token: undefined,
isShow: undefined,
userInfo: undefined
};
const initCallback = () => {
// From now on, you can write or read the variables in RNStorage synchronously
// equal to [console.log(await AsyncStorage.getItem('isShow'))]
console.log(RNStorage.isShow);
// equal to [ await AsyncStorage.setItem('token',TOKEN1343DN23IDD3PJ2DBF3==') ]
RNStorage.token = 'TOKEN1343DN23IDD3PJ2DBF3==';
// equal to [ await AsyncStorage.setItem('userInfo',JSON.stringify({ name:'rufeng', age:30})) ]
RNStorage.userInfo = {name: 'rufeng', age: 30};
};
XStorage.initStorage(RNStorage, AsyncStorage, initCallback);

React Native Function Call from Component

I am using Flatlist to show a list of Date. I have created a component DateList to view the list of item.
I have already called an API apiFreelancerDate. However, currently I am trying to call another function through the component (which I will later use it for second API call).
Date screen:
apiFreelancerDate = () => {
let self = this;
AsyncStorage.getItem('my_token').then((keyValue) => {
axios({
method: 'get',
url: Constants.API_URL + 'user_m/confirmation_date/',
params: {
freelancer_id: self.props.navigation.state.params.fr_id,
},
responseType: 'json',
headers: {
'X-API-KEY': Constants.API_KEY,
'Authorization': keyValue,
},
})
.then(function (response) {
self.setState({
dataSource: response.data.data,
isLoading: false,
});
})
.catch(function (error) {
console.log('Error Response: ', error.response);
});
}, (error) => {
console.log('Error', error) //Display error
});
}
//test function call
testFunc = () => {
console.log('test function called');
}
//test function call
viewFreelancerDate() {
const { dataSource } = this.state;
return (
<View>
{<FlatList
data={dataSource}
keyExtractor={({ id }, index) => index.toString()}
renderItem={({ item }) => <DateList {...item} functionCall={this.testFunc}/>}
/>}
</View>
);
}
DateList component:
<TouchableOpacity
style={{ flex: 1 }}
onPress={() => this.props.navigation.navigate('masterFreelancerTime')}
>
<Text style={styles.textStartDate}>{this.props.date_start}</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.activeBorder}
onPress={() => {
this.props.functionCall;
const newStatus = !this.state.status;
this.setState({
status: newStatus,
});
console.log('status: ', this.state.status);
console.log('Date: ', this.props.date_start);
}}
>
<Text style={styles.active_inactive}>{this.state.status ? "INACTIVE" : "ACTIVE"}</Text>
</TouchableOpacity>
I am very new to this so any help would be appreciated and I don't know if it's the right way to do it.
My main purpose is that I want to add a function call that will call an API which will show the state "INACTIVE" : "ACTIVE" based on what's in the database. And upon pressing/tapping, the "INACTIVE" : "ACTIVE" will toggle and at the same time it'll update it's value in the database.
You are mostly on right track. Although you may want to keep few things in mind.
e.g.
<TouchableOpacity
style={styles.activeBorder}
onPress={() => {
this.props.functionCall;
const newStatus = !this.state.status;
this.setState({
status: newStatus,
});
console.log('status: ', this.state.status);
console.log('Date: ', this.props.date_start);
}}
>
This is an overoptimisic assignment to state.status before api call finishes. What if the api call fails, in that case, your status is not in-sync with database. You can update the state without waiting for API to return the response, but you should update the state again in case API fails.
If you don't need optimistic state update, then you can do something like following:
Date screen:
//test function call
testFunc = () => {
console.log('test function called');
const dataSource = makeAPiCallAndGetUpdatedApiSource();
this.setState({ dataSource });
}
//test function call
viewFreelancerDate() {
const { dataSource } = this.state;
return (
<View>
{<FlatList
data={dataSource}
keyExtractor={({ id }, index) => index.toString()}
renderItem={({ item }) => <DateList {...item} functionCall={this.testFunc}/>}
/>}
</View>
);
}
DateList component:
constructor(props) {
this.state = {
status: props.status
};
}
...
componentDidUpdate() {
this.setState({ status: this.props.status })
}
...
<TouchableOpacity
style={styles.activeBorder}
onPress={() => {
this.props.functionCall();
}}
>
This way, after the api call, state of date screen will be updated ... which in turn will update DateList component, which will update the state of component.

Trouble with Type Error "array.slice is not a function

I am trying to slice an array thats holding Json data taken by yelps API and its giving me a type error message, saying that it is not a function. I believe its saying that the array is not a valid data type to use the method .slice() What is another method for passing Json data into a component as a prop?
state = { businesses: [], profileIndex: 0 };
componentWillMount() {
axios.get('https://api.yelp.com/v3/businesses/search', config)
.then(response => this.setState({ businesses: response.data }));
}
upcomingCard = () => {
this.setState({profileIndex: this.state.profileIndex + 1});
}
displayPlace = () => {
const {profileIndex, businesses} = this.state;
{businesses.slice(profileIndex, profileIndex + 1).map(place => {
return (
<SwipeCard
key={place.id}
place={place}
onSwipeOff={this.upcomingCard}
/>
);
})
}}
render() {
console.log(this.state.businesses);
return (
<View style={{flex:1}}>
{this.displayPlace()}
</View>
);
}
}
I got it, it was extra data that it did not need, So what I did was this
componentWillMount() {
axios.get('https://api.yelp.com/v3/businesses/search', config)
.then(response => this.setState({ businesses: response.data.businesses
}));
}
I had to go dipper into the Json data if you will.

Categories