I'm trying to get data from an array and using map function to render content. Look at
**{this.lapsList()}**
and the associated
**lapsList()**
function to understand what I'm trying to do. The result is nothing is displaying (Views under view, etc.) Here is my simplified code:
class StopWatch extends Component {
constructor(props) {
super(props);
this.state = {
laps: []
};
}
render() {
return (
<View style={styles.container}>
<View style={styles.footer}>
<View><Text>coucou test</Text></View>
{this.lapsList()}
</View>
</View>
)
}
lapsList() {
this.state.laps.map((data) => {
return (
<View><Text>{data.time}</Text></View>
)
})
}
_handlePressLap() {
console.log("press lap");
if (!this.state.isRunning) {
this.setState({
laps: []
})
return
}
let laps = this.state.laps.concat([{'time': this.state.timeElapsed}]);
this.setState({
laps: laps
})
console.log(laps);
}
}
Don't forget to return the mapped array , like:
lapsList() {
return this.state.laps.map((data) => {
return (
<View><Text>{data.time}</Text></View>
)
})
}
Reference for the map() method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Try moving the lapsList function out of your class and into your render function:
render() {
const lapsList = this.state.laps.map((data) => {
return (
<View><Text>{data.time}</Text></View>
)
})
return (
<View style={styles.container}>
<View style={styles.footer}>
<View><Text>coucou test</Text></View>
{lapsList}
</View>
</View>
)
}
lapsList() {
return this.state.laps.map((data) => {
return (
<View><Text>{data.time}</Text></View>
)
})
}
You forgot to return the map. this code will resolve the issue.
Related
I am trying to build a view which displays alarm codes - these are delivered to the app in a data array as follows:
alarm:[{ location: "Main Door", code:"123456"}, { location: "Back Door", code:"456789"}],
For each instance there could be 1 or many codes.
I am displaying the codes via this map function:
return this.state.alarmsOnSite.map((data, index) => {
return (
<View key={index}>
<Text style={GlobalStyles.SubHeading}>
Alarm: {data.location}
</Text>
<View style={[GlobalStyles.GreyBox, {position:'relative'}]}>
<Text style={GlobalStyles.starText}>
********
</Text>
<TouchableOpacity
style={CheckInStyles.eyeballImagePlacement}
>
<View style={CheckInStyles.eyeballImage} >
<Image
style={CheckInStyles.eyeballImageImage}
source={require('../images/icons/ico-eyeball.png')}
/>
</View>
</TouchableOpacity>
</View>
</View>
)
});
The brief states that on press of the touchable opacity - the stars should switch to display the code for 5 seconds only. I was thinking this would be easy with state - I could switch a display class on two Text objects to hide/show stars or code. But how do I do this with fixed state if I don't know how many alarm codes there will be? Can I use a dynamic state - is there such a thing - or does anyone have any other ideas for best approach in this situation please?
When setting up your state, include a property in the objects for whether they're showing:
this.state = {
alarmsOnSite: whereverYoureGettingTheDataNow.map(obj => ({...obj, showing: false})),
// ...
};
Then in response to a touch, set that flag to true and then back to false after five seconds. For instance, if the touch is on the ToucableOpacity itself (sorry, I don't know that component):
<View style={[GlobalStyles.GreyBox, {position:'relative'}]}>
<Text style={GlobalStyles.starText}>
{data.showing ? data.code : "********"}
</Text>
<TouchableOpacity
style={CheckInStyles.eyeballImagePlacement}
onTouch={() => this.showAlarm(data)}
>
<View style={CheckInStyles.eyeballImage} >
<Image
style={CheckInStyles.eyeballImageImage}
source={require('../images/icons/ico-eyeball.png')}
/>
</View>
</TouchableOpacity>
</View>
...where showAlarm is:
showAlarm(alarm) {
let updated = null;
this.setState(
({alarmsOnSite}) => ({
alarmsOnSite: alarmsOnSite.map(a => {
if (a === alarm) {
return updated = {...a, showing: true};
}
return a;
})
}),
() => {
setTimeout(() => {
this.setState(({alarmsOnSite}) => ({
alarmsOnSite: alarmsOnSite.map(a => a === updated ? {...a, showing: false} : a)
}));
}, 5000);
}
);
}
...or similar.
Here's a simplified example:
const whereverYoureGettingTheDataNow = [{ location: "Main Door", code:"123456"}, { location: "Back Door", code:"456789"}];
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
alarmsOnSite: whereverYoureGettingTheDataNow.map(obj => ({...obj, showing: false})),
// ...
};
}
showAlarm(alarm) {
let updated = null;
this.setState(
({alarmsOnSite}) => ({
alarmsOnSite: alarmsOnSite.map(a => {
if (a === alarm) {
return updated = {...a, showing: true};
}
return a;
})
}),
() => {
setTimeout(() => {
this.setState(({alarmsOnSite}) => ({
alarmsOnSite: alarmsOnSite.map(a => a === updated ? {...a, showing: false} : a)
}));
}, 5000);
}
);
}
render() {
return <div>
{this.state.alarmsOnSite.map((data, index) => (
<div key={index}>
{data.location}
<div onClick={() => this.showAlarm(data)}>
{data.showing ? data.code : "********"}
</div>
</div>
))}
</div>;
}
}
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.development.js"></script>
I'm having some trouble accessing an API that return a JSON. I want to list the information from the API on the screen but i'm getting that error : TypeError:undefined is not a function(near '... this.state.dataSource.map...').
That's what the API is returning:
{"rates":
{"CAD":1.3229349331,"HKD":7.7674203969,"ISK":127.0881402861,"PHP":50.564836179,"DKK":6.8944162437,
"HUF":308.8509460083,"CZK":22.882325796,"GBP":0.7682325796,"RON":4.4086755884,"SEK":9.7206275958,
"IDR":13652.496538994,"INR":71.3816335948,"BRL":4.3137055838,"RUB":63.4567604984,"HRK":6.8726349792,
"JPY":109.875403784,"THB":31.1748961698,"CHF":0.9820950623,"EUR":0.9229349331,"MYR":4.144993078,
"BGN":1.8050761421,"TRY":6.0491001384,"CNY":6.9815413013,"NOK":9.2637748039,"NZD":1.5564374712,
"ZAR":14.9603137979,"USD":1.0,"MXN":18.5734194739,"SGD":1.3887401938,"AUD":1.4886017536,
"ILS":3.4269497,"KRW":1183.848638671,"PLN":3.9335486848},
"base":"USD",
"date":"2020-02-17"}
Code:
import React, { Component } from 'react';
import { StyleSheet, Text, View, ActivityIndicator } from 'react-native';
export default class ConvertScreen extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: []
}
}
//API
componentDidMount() {
return fetch("https://api.exchangeratesapi.io/latest?base=USD")
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource:responseJson.rates
})
})
.catch((error) => {
return error
})
}
render() {
if (!this.state.isLoading) {
return (
<View style={style.container}>
<View>
{this.state.dataSource.map((item, id) => (
<Text> {item} </Text>
))}
</View>
</View >
);
}
else {
return (
<View style={style.container}>
<View style={style.viewInit}>
<ActivityIndicator />
</View>
</View>
);
}
}
}
How to iterate over the 'rates' data from the API?
You're getting this error because the .map function is only available on array.
It is {} but you are expecting [] so you may need to return array of a given object's own enumerable string-keyed property [key, value] pairs using Object.entries(obj):
Object.entries(stats).map(([key, value]) => console.log(key, value))
Using for..in loop is also a solution for your problem, it iterates over all enumerable properties of an object that are keyed by strings
for (const rate in rates) {
console.log(`${rate}: ${rates[rate]}`);
}
Trying to get FlatList to display data from Firebase.
Setup is correct and I can see the date in my console, but don't know how to visualise it.
I'd like to see 'recipeOne' 'recipeTwo' 'recipeThree' in the list.
I am sure I am missing something basic.
Here is the code
...
import {DataConfig} from '../data/DataConfig';
const firebaseApp = firebase.initializeApp(DataConfig);
globalTexts = require('../styles/Texts.js');
globalColors = require('../styles/Colors.js');
export default class ListSort extends Component {
constructor(props) {
super(props);
this.dataItem = firebaseApp.database().ref('recipes');
this.state = {
item: []
}
};
componentWillMount() {
this._createItemList();
};
_createItemList = (dataItem) => {
this.dataItem.on('value', (snapshot) => {
var itemList = [];
snapshot.forEach((doc) => {
itemList.push({
key:doc.key,
itemType: doc.toJSON()
});
this.setState({item: itemList});
console.log(this.state.item);
})
})
};
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.item}
renderItem={({item, index}) => (
<View style={styles.cell}>
<Text style={globalText.btnFlatPrimary}>{item.recipes}</Text>
</View>
)}
/>
</View>
)
}
}
and here is the data. The rules in Firebase are setup as read:true only.
{
"recipes": {
"recipeOne": {...
"recipeTwo": {...
"recipeThree": {...
}
}
I have a project in react-native (0.23) with Meteor 1.3 as back-end and want to display a list of contact items. When the user clicks a contact item, I would like to display a checkmark in front of the item.
For the connection to Meteor DDP I use the awesome library inProgress-team/react-native-meteor.
import Meteor, { connectMeteor, MeteorListView, MeteorComplexListView } from 'react-native-meteor';
class ContactsPicker extends React.Component {
constructor(props) {
super(props);
this.state = {
subscriptionIsReady: false
};
}
componentWillMount() {
const handle = db.subscribe("contacts");
this.setState({
subscriptionIsReady: handle.ready()
});
}
render() {
const {subscriptionIsReady} = this.state;
return (
<View style={gs.standardView}>
{!subscriptionIsReady && <Text>Not ready</Text>}
<MeteorComplexListView
elements={()=>{return Meteor.collection('contacts').find()}}
renderRow={this.renderItem.bind(this)}
/>
</View>
);
}
The first problem is, that subscriptionIsReady does not trigger a re-render once it returns true. How can I wait for the subscription to be ready and update the template then?
My second problem is that a click on a list item updates the state and should display a checkmark, but the MeteorListView only re-renders if the dataSource has changed. Is there any way to force a re-render without changing/ updating the dataSource?
EDIT 1 (SOLUTION 1):
Thank you #user1078150 for providing a working solution. Here the complete solution:
'use strict';
import Meteor, { connectMeteor, MeteorListView, MeteorComplexListView } from 'react-native-meteor';
class ContactsPicker extends React.Component {
getMeteorData() {
const handle = Meteor.subscribe("contacts");
return {
subscriptionIsReady: handle.ready()
};
}
constructor(props) {
super(props);
this.state = {
subscriptionIsReady: false
};
}
componentWillMount() {
// NO SUBSCRIPTION HERE
}
renderItem(contact) {
return (
<View key={contact._id}>
<TouchableHighlight onPress={() => this.toggleSelection(contact._id)}>
<View>
{this.state.selectedContacts.indexOf(contact._id) > -1 && <Icon />}
<Text>{contact.displayName}</Text>
</View>
</TouchableHighlight>
</View>
)
}
render() {
const {subscriptionIsReady} = this.data;
return (
<View>
{!subscriptionIsReady && <Text>Not ready</Text>}
<MeteorComplexListView
elements={()=>{return Meteor.collection('contacts').find()}}
renderRow={this.renderItem.bind(this)}
/>
</View>
);
}
}
connectMeteor(ContactsPicker);
export default ContactsPicker;
You have to do something like this :
getMeteorData() {
const handle = Meteor.subscribe("contacts");
return {
ready: handle.ready()
};
}
render() {
const { ready } = this.data;
}
I'm trying to render an X amount of photos depending on how long the OBJECT (photos) is. I've tried just appending data to a string but it doesn't work. Any good solutions?
var RenderPhotos = React.createClass({
getInitialState: function() {
return {
photos: this.props.photos
};
},
render: function(){
var photoHolder = "";
for(var i=0;i<this.props.photos.length;i++){
photoHolder += ("<View>
<Text>" { this.props.photos[0].description } "</Text>
</View>");
}
return (
{ photoHolder }
// <View>
// <Text> { this.props.photos[0].description } </Text>
// </View>
)
}
});
UPDATE December 2017: React v16 now allows you to return an array from the render function.
With a React class, your top-level render function MUST return a single component. However, inside your JSX, you can insert a single component, OR an array of components.
Like so:
render() {
var photoHolder = [];
for(var i=0;i<this.props.photos.length;i++){
photoHolder.push(
(<View>
<Text>{ this.props.photos[0].description }</Text>
</View>)
);
}
return (
<View>
{photoHolder}
</View>
)
}
EDIT: Here's another solution:
render() {
return (
<View>
{this.props.photos.map((photo, i) => {
return (
<View><Text>{photo.description}</Text></View>
);
})}
</View>
)
}