Firebase data not displayed on screen in react native - reactjs

I am currently working on a project in react native in which I have to retrieve data from firebase and display it in the form of Cards in the application. The data is retrieved and stored in the array(I have checked using alert and log). But when I try to map it to the render function. Nothing happens. No error.
constructor() {
super();
this.state={
items:[]
}
ref = firebase.database().ref("users/").orderByKey();
}
componentWillMount()
{
ref.on("value",(snapshot) => {
const itemss=[];
snapshot.forEach((childSnapshot) => {
var userkey = childSnapshot.key;
var userdata = childSnapshot.val();
itemss.push({
key:userkey,
ite:childSnapshot.val().ite,
});
});
this.setState({
items:itemss,
});
});
}
I think it might be a problem with async data collection from firebase but I am still unable to get this working. The code to show how it is displayed is:
<View>
{
this.state.items.map(function (i) {
return(
<View onPress={alert(i)} style=
{{marginLeft:100,height:50,
width:150,backgroundColor:'red'}}>
<Text style={{fontSize:50,color:'#000'}}>{i.ite}</Text>
</View>
);
})
}
</View>
Kindly tell me what am I missing in the above code.

Related

Binding data from a promise to a react component is inaccessible

I have two classes, one being a child component called article. I want to pass data to the article component after receiving data in the parent, which is my app.js. However, I am unable to drill down into the data from the response that I am setting in the App constructor--the data I am trying to access is undefined, but I am able to print the full response out fine, including the data that I am unable to drill down into. I would like to tie the articles array from the following JSON to my respective article components.
Data is being pulled from: https://newsapi.org/v2/top-headlines?sources=google-news&apiKey=edd0276dc8344c2abaeb40a3f6fb439f
class Article extends Component {
constructor(props) {
super(props);
this.props.title = props.title;
this.props.description = props.description;
this.props.url = props.url;
}
render() {
let pic = {
uri: 'https://cdn.vox-cdn.com/thumbor/M8Nb8mKymSEN59T2iDIe5XXiNTw=/0x146:2040x1214/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/11738447/VRG_ILLO_2761_Spacecraft_Sidebyside.jpg'
};
return (
<View style={styles.article}>
<Header/>
<Text>Two rockets launched within 15 minutes of each other Wednesday morning</Text>
<Image source={pic} style={{ height: 250}}/>
<Text numberOfLines={3}>Early in the morning on July 24th, rocket enthusiasts will have the lucky experience of being able to watch two launches at roughly the same time. Around 7:30AM ET, SpaceX is slated to launch one of its Falcon 9 rockets from the California coast, while Europe…</Text>
</View>
);
}
}
export default class App extends Component {
constructor(props) {
super(props);
let url = 'https://newsapi.org/v2/top-headlines?sources=google-news&apiKey=edd0276dc8344c2abaeb40a3f6fb439f';
this.state = fetch(url)
.then(function(response) {
response.text().then(function(text) {
console.log(text, "TEXT!!!");
return text;
});
});
}
render() {
return (
<ScrollView>
<Article/>
</ScrollView>
);
}
}
You shouldn't really be doing your fetch in the constructor.
Here's a working example to get you started:
import React from "react";
import ReactDOM from "react-dom";
const Article = ({ title, desc, url }) => {
return (
<React.Fragment>
<h1>{title}</h1>
<p>{desc}</p>
<img src={url} />
</React.Fragment>
);
};
let url =
"https://newsapi.org/v2/top-headlines?sources=google-news&apiKey=edd0276dc8344c2abaeb40a3f6fb439f";
class App extends React.Component {
state = {
articles: []
};
componentDidMount() {
fetch(url)
.then(res => res.json())
.then(data => {
this.setState({
articles: data.articles
});
});
}
render() {
if (this.state.articles.length === 0) {
return "Loading..";
}
const firstArticle = this.state.articles[0];
const { title, description, url } = firstArticle;
return <Article title={title} desc={description} url={url} />;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working CodeSandbox here.
A couple things wrong about this:
the return type of a Promise<T> is a Promise<T> unless you await, in which case it's T.
you don't assign to this.state, state changes in React are abstracted away via this.setState
Following,
this.state = fetch(url)...
should be changed to
fetch(url)
.then(response => response.text())
.then(text => this.setState({ text }))
pass in this.state.text (or rename it) as props to the child
edit: as Colin pointed out in the comments below, assigning to this.state is fine in the constructor (which you are doing). However, fetching data is better suited for post-mount lifecycle methods like componentDidMount

component return white blank screen

I am trying to check if the user authorised the use of the camera and location and if he did not, then a simple screen should be render that let him know about it. The function getting invoked but the return statement return blank screen instead of the component.
NOTE :
I tried to use 'backgroundColor: 'black' ti see if the rendered and I can't even see the black background.
CODE:
componentWillMount() {
Permissions.checkMultiple(['camera', 'location']).then(response => {
//response is an object mapping type to permission
console.log('permission check')
console.log('response.camera', response.camera)
console.log('response.location', response.location)
if (response.camera === 'denied' || response.location === 'denied') {
return (
<View>
<Text>
Sorry you cant use this app without allowing Location and Camera permmision
to do it just go to Setting/Keepr and allow Location and Camera access for this app
</Text>
<Button title={'go to settings'} onPress={Permissions.openSettings}></Button>
</View>
)
}
})
}
You are trying to return a JSX in a componentWillMount... and inside the Promise callback. This simply won't work as you need to return JSX from render method. You can use the react-state to do that. Example:
class MyComponent extends React.Component {
constructor(props) {
this.state = {
response: {}
};
}
componentWillMount() {
Permissions.checkMultiple(["camera", "location"]).then(response => {
this.setState({ response });
});
}
render() {
const { response } = this.state;
if (response.camera === "denied" || response.location === "denied") {
return (
<View>
<Text>
Sorry you cant use this app without allowing Location and Camera
permmision to do it just go to Setting/Keepr and allow Location and
Camera access for this app
</Text>
<Button title={"go to settings"} onPress={Permissions.openSettings} />
</View>
);
}
return <p>something</p>;
}
}

Cannot read property 'propertyName' of undefined

I'm working on a project in react-native where I have troubles of accessing an element inside an object array by passing it as a prop where I want it to be used. Requirement is to get the name property out and set it to a text inside a flatlist.
The structure of my object array as follow.
[
{
"media1":[
{"name":"Lynn"},
{"name":"Michelle"},
{"name":"Carter"}
]
},
{
"media2":[
{"price":"23"},
{"price":"76"},
{"price":"39"}
]
}
]
This is how is pass this object array as a prop where I want it to be used
return (
<View>
<AlbumDetail data = {this.state.allData}/>
</View>
);
This is where I want it to be used
const AlbumDetail = (props) => {
return (
<View>
{console.log(props.data[0])} //Working
{console.log(props.data[0].media1[0].name)} //Not working
// Requirement as bellow
<Text>{wants to set the "name" here}</Text>
<Text>{wants to set the "price" here}</Text>
</View>
);
};
How can I achieve this ??
You might want to place two missing comma's.
One after:
{"name":"Michelle"}
And one after
{"price":"76"}
AlbumDetail has no way to know it has a property called data. You need to write AlbumDetail function as a React.Component class.
You are passing a JSON object into AlbumDetail, you need to call JSON.parse(data) before use it. UPDATE: .then(resp => resp.json()) is used to parse json.
Place console.log before return. The object you returned should be pure JSX components.
The code below should solve your problem:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const url =
'http://purelight-prod.appspot.com/api/user/v2/browse/homescreendata';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: undefined,
};
}
componentDidMount() {
fetch(url)
.then(resp => resp.json())
.then(respJson => {
this.setState({
data: respJson,
});
})
.catch(err => {
console.error(err);
});
}
render() {
return (
<View style={{ flex: 1 }}>
<TestView data={this.state.data} />
</View>
);
}
}
class TestView extends React.Component {
render() {
!!this.props.data && console.log(console.log(data[0].healer[0].healerid));
return (
<View>
<Text>Hello World!</Text>
</View>
);
}
}
Edit:
Use componentDidMount(), because we like to display something (loading icon, etc), and then update the View when data arrived.
This is an async task. The data has to be held until it arrived. I use !!this.props.data && ..., so it only displays when it is not undefined.
Since the API response is a relatively big package, it will be much easier to work with, if you use TypeScript and create an object class to parse it.
I don't think the API helper package provides correct response in your code.

React Native Props Not Updating

I'm pretty new to React and I'm running into an issue with updating my props in a container. I'm updating my state using WebSockets and the props are being updated in my mapStateToProps function, but my componentWillReceiveProps is not being called despite that.
When the sockets emit, updateGameVariables calls an Action sending the emitted data, which then goes to my reducer which is using the Spread Operator to update state. And then mapStateToProps logs the proper data (which is updating).
Here is the main file I am dealing with (everything is being properly imported I just wanted to cut down on code):
class GameList extends Component {
constructor(props){
super(props);
this.ds = new ListView.DataSource({ rowHasChanged: (r1,r2) => r1 !== r2})
const { games } = props;
this.state = {
games: this.ds.cloneWithRows(games)
}
this.socket = SocketIOClient('http://localhost:9615',{ transports: ['websocket'], query: 'r_var=17' });
this.socket.on('appGames', function(results){
props.dispatch(updateGameVariables(results))
});
}
componentWillReceiveProps(nextProps){
this.setState({
games: this.ds.cloneWithRows(nextProps.games)
})
}
render() {
let { games } = this.state;
return (
<View style={styles.list}>
<ListView
dataSource={games}
renderRow={(rowData, sectionID, rowID) =>
<TouchableOpacity>
<GameIntro key={rowID} game={rowData} />
</TouchableOpacity>
}
>
</ListView>
</View>
)
}
}
function mapStateToProps({games}){
return {
games: games.games, // Array
// rand: Math.random()
}
}
function mapDispatchToProps(dispatch) {
let actions = bindActionCreators({ updateGameVariables });
return { ...actions, dispatch };
}
export default connect(mapStateToProps, mapDispatchToProps)(GameList)
And here is the GameIntro component that is being referenced.
export default props => {
let { game } = props;
return (
<View style={styles.itemContainer}>
<View style={styles.game_timerow}>
<Text style={styles.game_time}>{game.period} {game.minutes}:{game.seconds}</Text>
</View>
</View>
)
}
Also as a note, when I have the rand: Math.random() function uncommented everything updates properly. And so I feel like react simply isn't picking up on updates to the games array. Or I am just not understanding a core concept of React. Any help would be greatly appreciated!
It's likely that you have a mutation problem in your reducer code, and because of that React see the games array as the same, then decide to not update the rendering. It explains why
rand: Math.random()
help React to realize that there is update in the props object and trigger re-render.
http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html might help.

React component does not react to mobx observable data

I am using mobX for my react native project. Please consider this store class:
class Birds {
#observable listOne = [];
#observable fetchingListOne = false;
#observable fetchErrorOne = '';
#action setListOne = () => {
this.fetchingListOne = true;
api.getList()
.then((data) => {
this.listOne.replace(data);
this.fetchingListOne = false;
})
.catch((error) => {
this.fetchingListOne = false;
this.fetchErrorOne = error;
});
};
}
And this the react component:
#inject('BirdStore') #observer
export default class Flat extends React.Component {
componentDidMount() {
this.props.BirdStore.setListOne();
}
_renderHeader = () => {
return <Text style={styles.listHeaderText}>
Hello {this.props.BirdStore.listOne.length} is {this.props.BirdStore.fetchingListOne.toString()}
</Text>;
};
_renderItem = ({item}) => {
return <Text style={styles.item}>{item.name}</Text>
};
_renderFooter = () => {
if (this.props.BirdStore.fetchingListOne) {
return <ActivityIndicator/>
}
else {
return null
}
};
render() {
const dataSource = this.props.BirdStore.listOne.slice();
return (
<View style={styles.container}>
<Text>Fetching: {this.props.BirdStore.fetchingListOne.toString()}</Text>
<FlatList
style={styles.listContainer}
ListHeaderComponent={this._renderHeader}
data={dataSource}
renderItem={this._renderItem}
keyExtractor={(item, i) => item.id}
ListFooterComponent={this._renderFooter}
/>
</View>
)
}
}
From above it looks to me that:
When the Flat component mounts, it call the method of the store setListOne().
setListOne() sets fetchingListOne to true and makes an api call.
On the component side, when the fetchingListOne is true, the ActivityIndicator displays, and in the ListHeaderComponent it should display true.
On the store side, after successful/unsuccessful response, it sets fetchingListOne to false.
Finally on the component side, because fetchingListOne is set to false, ActivityIndicator should not display and in the ListHeaderComponent it should display false.
However, this is not what's happening. Here when the setListOne() method is called, after it sets the fetchingListOne to true, the component does not react to the changes made after api call. And the ActivityIndicator keeps displaying and in ListHeaderComponent its displaying true.
What am I doing wrong here? Could you please help me. Thank you
Update
I have added a Text component before the FlatList. Adding a Text component or console logging inside the component class's render method does makes the FlatList react to the changes. I don't know why this is happening though.
The problem you are running into here most probably, is that although Flat is an observer component, FlatList is not (it's an built-in component after all). In this setup _renderFooter and the others are part are rendered by render of FlatList, but not of FlatList. Hence they are not part of the lifecycle of Flat, but of FlatList and as such are not tracked by Mobx
There are two ways to fix this, both pretty simple:
1) declare _renderItem as observer component:
_renderItem = observer(({item}) =>
<Text style={styles.item}>{item.name}</Text>
);
2) use an inline anonymous Observer component:
_renderItem = ({item}) =>
<Observer>{
() => <Text style={styles.item}>{item.name}</Text>}
</Observer>

Resources