I'm working with ReactNative for the first time. I'm trying to build a ListView based off what a api returns. Here is my component:
'use strict';
var React = require('react-native');
var api = require('../utils/api');
var {
ActivityIndicatorIOS,
ListView,
StyleSheet,
Text,
TextInput,
TouchableHighlight,
View
} = React;
class CheckIn extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
search(searchTerm) {
api.search(searchTerm)
.then((response) => {
this.setState({
isLoading: false,
dataSource: this.state.dataSource.cloneWithRows(response)
})
});
}
renderCustomRow(row) {
return (
<TouchableHighlight style={styles.row}>
<View style={styles.container}>
<Text>{row.name}</Text>
</View>
</TouchableHighlight>
);
}
render() {
return (
<View style={{marginTop: 65}}>
<TextInput
style={styles.search}
placeholder="Search"
onChangeText={(text) => {
this.search(text);
this.setState({isLoading: true});
}}/>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderCustomRow.bind(this)}
style={styles.listView} />
<ActivityIndicatorIOS
animating={this.state.isLoading}
color='#111'
size="large"></ActivityIndicatorIOS>
</View>
)
}
}
var styles = StyleSheet.create({
search: {
height: 40,
padding: 5,
borderBottomWidth: 2,
borderBottomColor: '#000'
}
});
module.exports = CheckIn;
When I type one letter into the search bar, the activity indicator appears, the api returns a valid JSON response (checked with console.log), and then the activity indicator should disappear, but it doesn't. If I type a second letter, I get the following error:
Cannot read property '_currentElement' of null
Anything I seem to be missing?
I'm using ReactNative 0.14, npm is version 3.5
Related
I want to render my contact list in my app using expo-contacts, the list display for about 2 seconds, then i get typeError: undefined is not an object (evaluating 'item.phoneNumbers[0]'). I have checked the documentation to see if I made any errors, but i could not find any. Does anyone have a work around this
below is my code
ContactList.js
import React, { Component } from "react";
import {
View,
Text,
Platform,
StatusBar,
FlatList,
StyleSheet,
ActivityIndicator
} from "react-native";
import * as Contacts from "expo-contacts";
import * as Permissions from "expo-permissions";
class ContactList extends Component {
static navigationOptions = {
header: null
};
constructor(props) {
super(props);
this.state = {
isLoading: false,
contacts: []
};
}
async componentDidMount() {
this.setState({
isLoading: true
});
this.loadContacts();
}
loadContacts = async () => {
const permissions = await Permissions.askAsync(Permissions.CONTACTS);
if (permissions.status !== "granted") {
return;
}
const { data } = await Contacts.getContactsAsync({
fields: [Contacts.Fields.PhoneNumbers, Contacts.Fields.Emails]
});
this.setState({
contacts: data,
isLoading: false
});
};
handleBack() {
this.props.navigation.goBack();
}
renderItem = ({ item }) => (
<View style={{ minHeight: 70, padding: 5 }}>
<Text>
{item.firstName}
{item.lastName}
</Text>
<Text>{item.phoneNumbers[0].digits}</Text>
</View>
);
render() {
const { isLoading, contacts } = this.state;
let emptyContact = null;
emptyContact = (
<View style={styles.emptyContactStyle}>
<Text style={{ color: "red" }}>No Contacts Found</Text>
</View>
);
return (
<SafeAreaView style={styles.contentWrapper}>
<View style={styles.contentWrapper}>
{isLoading ? (
<View style={styles.isLoadingStyle}>
<ActivityIndicator size="large" color="#2484E8" />
</View>
) : null}
<FlatList
data={contacts}
renderItem={this.renderItem}
keyExtractor={(item, index) => index.toString()}
ListEmptyComponent={emptyContact}
/>
</View>
</SafeAreaView>
);
}
}
Here is a new answer because the previous one was off topic. The error occurs because the displayed contact doesn't have a phoneNumber.
You should check first that a phone number exists before displaying it:
renderItem = ({ item }) => (
<View style={{ minHeight: 70, padding: 5 }}>
<Text>
{item.firstName}
{item.lastName}
</Text>
<Text>
{item.phoneNumbers && item.phoneNumbers[0] && item.phoneNumbers[0].digits}
</Text>
</View>
);
I am building an app in react native and I am using Lisview to display some data for some strange reason the endReached is triggered itself without me scrolling the listview and the listView ends up displaying all items at first like I have incremented page value each time, also i get duplicate results for first api call with value page 1.
Code:
import React, {Component} from 'react';
import {Alert, ListView, Text, View} from 'react-native';
import categoryApi from './category.api';
import styles from './category.styles';
import CategoryItem from './category.items.component';
import ShopsNear from "../listshops/list-shops.component";
export default class Category extends React.Component {
constructor(props) {
super(props);
this.state = {
rawData: [],
isLoading: false,
categories: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
}),
page: 1,
};
}
static navigationOptions = {
headerTitle: 'Categories',
title: 'Categories',
};
componentDidMount() {
this.fetchCategories();
}
fetchCategories() {
this.setState({isLoading: true});
categoryApi.getOne(this.state.page).then(response => {
if (response.data) {
this.setState({
rawData: this.state.rawData.concat(response.data.data),
categories: this.state.categories.cloneWithRows(this.state.rawData.concat(response.data.data)),
isLoading: false,
});
} else {
this.setState({isLoading: false});
Alert.alert(
'Something wrong happened!',
'My Alert Msg',
[],
{cancelable: true}
)
}
});
}
componentWillMount() {
}
showMore = () => {
this.setState({page: this.state.page + 1});
console.log("End reached... page: " + this.state.page);
this.fetchCategories();
};
render() {
const {navigate} = this.props.navigation;
return (
<View style={styles.container}>
<View style={styles.projektiHeader}>
<Text style={styles.projekti}>VALITSE PROJEKTI</Text>
</View>
<View style={styles.categoriesList}>
<ListView
dataSource={this.state.categories}
renderRow={(rowData) => <CategoryItem navigate={navigate} item={rowData}/>}
renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator}/>}
onEndReached={this.showMore}
/>
</View>
<View style={styles.shopsNear}>
<ShopsNear navigate={navigate}/>
</View>
</View>
);
}
}
Basically showMore() is itself, Anyone knows what's happening here?
I am trying to achieve so everytime I scroll and reaches the end of listview to call the showMore function which will fetch data from an API.
check the onEndReachedThreshold props for listView component.
Teaching myself react native by making a chat app, now when someone clicks on a delete button next to the message (not visible in this code as it's irrelevant), it deletes it from the database but I need to make the changes here in the app.
For this I have set a ref one each of the <Message/> components and I have the ref which matches the ref on the component. But i need to target the actual component node which has that ref.
Is refs the way to go about doing this? If not, what else can I do?
Many thanks
edit: Full code:
import React, { Component } from "react"
import { ListView, View, Text, StyleSheet, TextInput, TouchableHighlight } from "react-native"
import * as firebase from 'firebase';
import Icon from 'react-native-vector-icons/FontAwesome'
import { Hideo } from 'react-native-textinput-effects';
import Message from './Message'
export default class Chat extends Component {
constructor() {
super();
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.onSend = this.onSend.bind(this);
this.state = {
messages: this.ds.cloneWithRows([]),
messageContent: '',
};
}
componentWillMount() {
// Get all of the messages from firebase database and push them into "nodes" array which then gets set in the "messages" state above in handleChat.
const chatRef = firebase.database().ref().child('general');
this.messages = [];
chatRef.on('child_added', snap => {
this.messages.push({user: snap.val().user.name,
text: snap.val().text,
messageId: snap.key
})
this.handleChat(this.messages);
}
).bind(this);
// this is what happens when someone removes a comment
chatRef.on('child_removed', snap => {
const messageId = snap.key; // <- This is the key in the database, for example: '-KVZ_zdbJ0HMNz6lEff'
this.removeMessage(messageId);
})
}
removeMessage(messageId){
let messages = this.messages.filter(message => message.messageId !== messageId);
this.handleChat(messages);
}
handleChat(messages) {
this.setState({messages: this.ds.cloneWithRows(messages)})
}
onSend(messages) {
const generalRef = firebase.database().ref().child('general');
const user = firebase.auth().currentUser;
generalRef.push(
{
_id: 1,
text: this.state.messageContent,
createdAt: new Date().getTime(),
user: {
_id: 2,
name: user.displayName,
avatar: 'http://mdepinet.org/wp-content/uploads/person-placeholder.jpg'
}
});
this.setState({messageContent: ''})
}
removeMessage(messageId){
let messages = this.messages.filter(message => message.messageId !== messageId);
this.handleChat(messages);
}
render() {
return (
<View style={{flex: 1, alignItems: 'flex-end'}}>
<ListView
style={{ marginBottom: 60 }}
enableEmptySections={true}
dataSource={this.state.messages}
renderRow={message => <Message name={message.user} text={message.text}/> }/>
<Hideo
style={{position: 'absolute', bottom: 0}}
onChangeText={messageContent => this.setState({messageContent})} value={this.state.messageContent} placeholder="Name"
iconClass={Icon}
iconName={'envelope'}
iconColor={'white'}
iconBackgroundColor={'#222'}
inputStyle={{ color: '#464949' }}
/>
<TouchableHighlight onPress={this.onSend} style={{position: 'absolute', alignItems: 'center', bottom: 10, right: 10, borderRadius: 10, backgroundColor: '#d4af37'}}>
<Text style={{color: 'whitesmoke', fontSize: 20, padding: 5}}>Send</Text>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
username: {
fontFamily: 'AvenirNext-Bold'
},
comment: {
fontFamily: 'AvenirNext-Regular'
},
bubble: {
flex: 1,
width: 250,
backgroundColor: '#f5f5f5',
margin: 15,
padding: 10,
borderRadius: 20
}
})
Using refs to ListView row items is not a Good idea. As Elmeister told we just need to remove the message elements from array and update the ListView datasource in order to delete a message from ListView.
Here is a sample code which will give you an idea of how you can do the same in your app.
import React, {
Component
} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
TouchableHighlight,
} from 'react-native';
class StackOverflow extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.messages = this.getMessages();
this.state = {
dataSource: ds.cloneWithRows(this.messages)
};
}
getMessages() {
let arr = [];
for (let i = 0; i < 100; i++) {
arr.push({
user: 'User ' + i,
text: 'This is a sample user message ' + i,
messageId: 'messageId' + i,
});
}
return arr;
}
onDeletePress(messageId) {
this.messages = this.messages.filter(message => message.messageId !== messageId);
this.setState({
dataSource: this.state.dataSource.cloneWithRows(this.messages)
});
}
renderRow(rowData) {
return (
<View style={{padding:8}}>
<Text>{rowData.user}</Text>
<Text>{rowData.text}</Text>
<TouchableHighlight
style={{alignSelf :'flex-end',backgroundColor:'red'}}
onPress={this.onDeletePress.bind(this,rowData.messageId)}>
<Text>Delete</Text>
</TouchableHighlight>
</View>
);
}
render() {
return (
<View style={{flex: 1, paddingTop: 22}}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
/>
</View>
);
}
}
AppRegistry.registerComponent('StackOverflow', () => StackOverflow);
In the above example, We are creating dummy messages and saving them in messages array and updating dataSource with messages.
When onDeletePress is called we pass the messageId to that method, and below line
this.messages = this.messages.filter(message => message.messageId !== messageId);
removes the message from messages array. Then we update the dataSource state which will update the ListView.
In your code probably these changes you will have to make,
Change handleChat
handleChat(messages) {
this.setState({messages: this.ds.cloneWithRows(messages)})
}
Update Chat component like below,
import React, {Component} from "react"
import {ListView, View, Text, StyleSheet, TextInput, TouchableHighlight} from "react-native"
import * as firebase from 'firebase';
import Icon from 'react-native-vector-icons/FontAwesome'
import {Hideo} from 'react-native-textinput-effects';
import Message from './Message'
export default class Chat extends Component {
constructor() {
super();
this.chatRef = firebase.database().ref().child('general');
this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.onSend = this.onSend.bind(this);
this.state = {
messages: this.ds.cloneWithRows([]),
messageContent: '',
};
}
componentWillMount() {
// Get all of the messages from firebase database and push them into "nodes" array which then gets set in the "messages" state above in updateMessageList.
this._messages = [];
this.chatRef.on('child_added', snap => {
this._messages.push({
user: snap.val().user.name,
text: snap.val().text,
messageId: snap.key
});
this.updateMessageList(this._messages);
}
).bind(this);
// this is what happens when someone removes a comment
this.chatRef.on('child_removed', snap => {
const messageId = snap.key; // <- This is the key in the database, for example: '-KVZ_zdbJ0HMNz6lEff'
this.removeMessage(messageId);
}).bind(this);
}
removeMessage(messageId) {
this._messages = this._messages.filter(message => message.messageId !== messageId);
this.updateMessageList(this._messages);
}
updateMessageList(messages) {
this.setState({messages: this.ds.cloneWithRows(messages)})
}
onSend(messages) {
const user = firebase.auth().currentUser;
this.chatRef.push(
{
_id: 1,
text: this.state.messageContent,
createdAt: new Date().getTime(),
user: {
_id: 2,
name: user.displayName,
avatar: 'http://mdepinet.org/wp-content/uploads/person-placeholder.jpg'
}
});
this.setState({messageContent: ''})
}
render() {
return (
<View style={{flex: 1, alignItems: 'flex-end'}}>
<ListView
style={{ marginBottom: 60 }}
enableEmptySections={true}
dataSource={this.state.messages}
renderRow={message => <Message name={message.user} text={message.text}/> }/>
<Hideo
style={{position: 'absolute', bottom: 0}}
onChangeText={messageContent => this.setState({messageContent})} value={this.state.messageContent}
placeholder="Name"
iconClass={Icon}
iconName={'envelope'}
iconColor={'white'}
iconBackgroundColor={'#222'}
inputStyle={{ color: '#464949' }}
/>
<TouchableHighlight onPress={this.onSend}
style={{position: 'absolute', alignItems: 'center', bottom: 10, right: 10, borderRadius: 10, backgroundColor: '#d4af37'}}>
<Text style={{color: 'whitesmoke', fontSize: 20, padding: 5}}>Send</Text>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
username: {
fontFamily: 'AvenirNext-Bold'
},
comment: {
fontFamily: 'AvenirNext-Regular'
},
bubble: {
flex: 1,
width: 250,
backgroundColor: '#f5f5f5',
margin: 15,
padding: 10,
borderRadius: 20
}
});
Suppose I have a simple React Native app like so:
'use strict';
var React = require('react-native');
var {
AppRegistry,
Text,
TouchableHighlight,
View,
} = React;
var ReactProject = React.createClass({
_onPressOut: function() {
// What do we do here?
},
render() {
return (
<View>
<Text>This text should be before</Text>
<Text>This text should be after</Text>
<TouchableHighlight onPressOut={this._onPressOut}>
<Text>Tap Me</Text>
</TouchableHighlight>
</View>
);
}
});
AppRegistry.registerComponent('ReactProject', () => ReactProject);
How can I dynamically insert a component between the first and second Text tags when the TouchableHighlight is pressed?
Try creating an array and attaching it to the state. You can then push items to the array, and reset the state.
https://rnplay.org/apps/ymjNxQ
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight
} = React;
var index = 0
var SampleApp = React.createClass({
getInitialState(){
return { myArr: [] }
},
_onPressOut() {
let temp = index ++
this.state.myArr.push(temp)
this.setState({
myArr: this.state.myArr
})
},
render() {
let Arr = this.state.myArr.map((a, i) => {
return <View key={i} style={{ height:40, borderBottomWidth:2, borderBottomColor: '#ededed' }}><Text>{ a }</Text></View>
})
return (
<View style={styles.container}>
<Text>First</Text>
{ Arr }
<Text>Second</Text>
<TouchableHighlight style={ styles.button } onPress={ () => this._onPressOut() }>
<Text>Push</Text>
</TouchableHighlight>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
marginTop:60
},
button: {
height:60,
backgroundColor: '#ededed',
marginTop:10,
justifyContent: 'center',
alignItems: 'center'
}
});
AppRegistry.registerComponent('SampleApp', () => SampleApp);
I've set up a working example here.
In react or react native the way component hide/show or add/remove does not work like in android or iOS. Most of us think there would be the similar stratedgy like
View.hide = true or parentView.addSubView(childView
But the way react native work is completely different. The only way to acheive this kind of functionality is to include your component in your DOM or remove from DOM.
Here in this example I am going set the visibility of text view based on the button click.
enter image description here
The idea behind this task is the create a state variable called state having the initial value set to false when the button click event happens then it value toggles. Now we will use this state variable during the creation of component.
import renderIf from './renderIf'
class fetchsample extends Component {
constructor(){
super();
this.state ={
status:false
}
}
toggleStatus(){
this.setState({
status:!this.state.status
});
console.log('toggle button handler: '+ this.state.status);
}
render() {
return (
<View style={styles.container}>
{renderIf(this.state.status)(
<Text style={styles.welcome}>
I am dynamic text View
</Text>
)}
<TouchableHighlight onPress={()=>this.toggleStatus()}>
<Text> touchme </Text>
</TouchableHighlight>
</View>
);
}
}
the only one thing to notice in this snippet is renderIf which is actually a function which will return the component passed to it based on the boolean value passed to it.
renderIf(predicate)(element).
renderif.js
'use strict';
const isFunction = input => typeof input === 'function';
export default predicate => elemOrThunk =>
predicate ? (isFunction(elemOrThunk) ? elemOrThunk() : elemOrThunk) : null;
With React components you don't want to think of actions reaching into the DOM and inserting components - you want to think components responding to actions. Theoretically, this component is already composed and ready, it just needs to know if it should be rendered or not:
var ReactProject = React.createClass({
getInitialState() {
// our *state* dictates what the component renders
return {
show: false
};
}
_onPressOut: function() {
// update our state to indicate our "maybe" element show be shown
this.setState({show: !this.state.show});
},
maybeRenderElement() {
if (this.state.show) {
// depending on our state, our conditional component may be part of the tree
return (
<Text>Yay!</Text>
);
}
return null;
}
render() {
return (
<View>
<Text>This text should be before</Text>
{this.maybeRenderElement()}
<Text>This text should be after</Text>
<TouchableHighlight onPressOut={this._onPressOut}>
<Text>Tap Me</Text>
</TouchableHighlight>
</View>
);
}
});
I've also made a helper that makes it easy to conditionally render things, render-if
renderIf(this.state.show)(
<Text>Yay</Text>
)
ECMA6 Syntax
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity,
TouchableHighlight
} from 'react-native';
export default class fourD extends Component {
constructor(props) {
super(props);
let ele1 = (
<View key={1}>
<Text>Element {1}</Text>
<TouchableOpacity onPress={ () => this._add() }>
<Text>Add</Text>
</TouchableOpacity>
</View>
);
this.state = {
ele: [],
key: 1
}
this.state.ele.push(ele1);
}
_add(){
let key = this.state.key + 1;
let ele2 = (
<View key={key}>
<Text>Element {key}</Text>
<TouchableOpacity onPress={ () => this._add() }>
<Text>Add</Text>
</TouchableOpacity>
</View>
);
let ele = this.state.ele;
ele.push(ele2);
this.setState({ ele: ele,key : key})
}
render() {
return (
<View style={styles.container}>
<Text>This text should be before</Text>
{ this.state.ele }
<Text>This text should be after</Text>
<TouchableHighlight onPressOut={ () => this._add() }>
<Text>Tap Me</Text>
</TouchableHighlight>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
}
})
I'm currently trying to learn React Native based on this Tutorial: http://www.appcoda.com/react-native-introduction/
While copying most of the Code (small changes in text) I got this error:
Error: Cannot read property 'push' of undefined
This error occurs if I try to push a new Navigator View. Here is the striped down code (full code at the end but thought it's more readable to have just a short version here):
<TouchableHighlight onPress={() => this._rowPressed(eve)} >
_rowPressed(eve) {
this.props.navigator.push({
title: "Property",
component: SingleEvent,
passProps: {eve}
});
}
Maybe somebody can explain me why the this.props.navigator is undefined and how I can use it. I'm sorry for this basic question but I searched a lot and couldn't find a answer to this problem yet. I tryed to .bind(this) to the _rowPressed function and also rewrote everything to a NavigatorIOS View but nothing worked yet.
Would be nice if somebody could explain it to me.
All the best
Daniel
Full Error report:
Error: Cannot read property 'push' of undefined
stack:
Dates._rowPressed index.ios.bundle:52051
Object._createClass.value.React.createElement.onPress index.ios.bundle:52033
React.createClass.touchableHandlePress index.ios.bundle:41620
TouchableMixin._performSideEffectsForTransition index.ios.bundle:39722
TouchableMixin._receiveSignal index.ios.bundle:39640
TouchableMixin.touchableHandleResponderRelease index.ios.bundle:39443
executeDispatch index.ios.bundle:15431
forEachEventDispatch index.ios.bundle:15419
Object.executeDispatchesInOrder index.ios.bundle:15440
executeDispatchesAndRelease index.ios.bundle:14793
URL: undefined
line: undefined
message: Cannot read property 'push' of undefined
Code Of the Parent View which gets included into the main View via TabBarIOS:
'use strict';
var React = require('react-native');
var singleEvent = require('./singleEvent');
var REQUEST_URL = 'http://***/dates/24-09-2015.json';
var {
Image,
StyleSheet,
Text,
View,
Component,
ListView,
NavigatorIOS,
TouchableHighlight,
TabBarIOS,
ActivityIndicatorIOS
} = React;
var styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
padding: 10
},
thumbnail: {
width: 53,
height: 81,
marginRight: 10
},
rightContainer: {
flex: 1
},
title: {
fontSize: 16,
marginBottom: 8
},
author: {
color: '#656565',
fontSize: 12
},
separator: {
height: 1,
backgroundColor: '#dddddd'
},
listView: {
backgroundColor: '#F5FCFF'
},
loading: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
class Dates extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData),
isLoading: false
});
})
.done();
}
render() {
if (this.state.isLoading) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderEvent.bind(this)}
style={styles.listView}
/>
);
}
renderLoadingView() {
return (
<View style={styles.loading}>
<ActivityIndicatorIOS size='large'/>
<Text>Loading Events...</Text>
</View>
);
}
renderEvent(eve) {
return (
<TouchableHighlight onPress={() => this._rowPressed(eve).bind(this)} underlayColor='#dddddd'>
<View>
<View style={styles.container}>
<View style={styles.rightContainer}>
<Text style={styles.title}>{eve.value.name}</Text>
<Text style={styles.author}>{eve.value.location}</Text>
</View>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
_rowPressed(eve) {
console.log(eve, this.props);
this.props.navigator.push({
title: "Property",
component: SingleEvent,
passProps: {eve}
});
}
}
module.exports = Dates;
Single View which should be included if the ListView was clicked:
'use strict';
var React = require('react-native');
var {
StyleSheet,
Text,
TextInput,
View,
TouchableHighlight,
ActivityIndicatorIOS,
Image,
Component
} = React;
var styles = StyleSheet.create({
description: {
fontSize: 16,
backgroundColor: 'white'
},
title : {
fontSize : 22
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
class SingleEvent extends Component {
render() {
var eve = this.props.eve;
var description = (typeof eve.value.description !== 'undefined') ? eve.value.description : '';
return (
<View style={styles.container}>
<Text style={styles.title}>{eve.value.name}</Text>
<Text style={styles.description}>{description}</Text>
</View>
);
}
}
module.exports = SingleEvent;
index.ios.js where all the views get combined:
'use strict';
var React = require('react-native');
var Dates = require('./Dates');
//var Eventlist = require('./eventlist');
var NearYou = require('./NearYou');
var icons = [];
icons['place'] = require('image!ic_place_18pt');
icons['reorder'] = require('image!ic_reorder_18pt');
icons['grade'] = require('image!ic_grade_18pt');
icons['people'] = require('image!ic_group_18pt');
var {
Image,
AppRegistry,
StyleSheet,
Text,
View,
ListView,
TouchableHighlight,
TabBarIOS,
Component
} = React;
class allNightClub extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: 'dates'
};
}
render() {
return (
<TabBarIOS selectedTab={this.state.selectedTab}>
<TabBarIOS.Item
selected={this.state.selectedTab === 'dates'}
icon={icons['reorder']}
title= 'Events'
onPress={() => {
this.setState({
selectedTab: 'dates'
});
}}>
<Dates navigator={navigator} />
</TabBarIOS.Item>
<TabBarIOS.Item
selected={this.state.selectedTab === 'nearyou'}
title= 'Favorites'
icon={icons['grade']}
onPress={() => {
this.setState({
selectedTab: 'nearyou'
});
}}>
<NearYou navigator={navigator} />
</TabBarIOS.Item>
<TabBarIOS.Item
selected={this.state.selectedTab === 'nearyou'}
title= 'Near You'
icon={icons['place']}
onPress={() => {
this.setState({
selectedTab: 'nearyou'
});
}}>
<NearYou navigator={navigator} />
</TabBarIOS.Item>
<TabBarIOS.Item
selected={this.state.selectedTab === 'nearyou'}
title= 'People'
icon={icons['people']}
onPress={() => {
this.setState({
selectedTab: 'nearyou'
});
}}>
<NearYou navigator={navigator} />
</TabBarIOS.Item>
</TabBarIOS>
);
}
}
AppRegistry.registerComponent('allNightClub', () => allNightClub);
in your index.ios.js you're referencing a navigator here which isn't set at that moment.
<Dates navigator={navigator} />
So, as I've understood you have to options to work with NavigatorIOS:
1. NavigatorIOS as a child of your Tab
You need to define a navigator as a child of your TabViewItems which itself loads the appropriate view:
var styles = StyleSheet.create({
container: {
flex: 1,
}
});
<TabBarIOS.Item>
<NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Dates',
component: Dates,
}}
/>
</TabBarIOS.Item>
2. NavigatorIOS as the root Element
class allNightClub extends Component {
render() {
return (
<NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Index',
component: Index
}}
/>
);
}
}
That's the way it's worked for me. I put the original code of index.ios.js into Index.js and also did the following changes:
Index.js
<Dates
navigator={this.props.navigator}
/>
Dates.js
<TouchableHighlight onPress={() => this._rowPressed(eve)} underlayColor='#dddddd'>
From what I can deduct, your call to this.props.navigator should work, even without the bind-statements.
My first thoughts would be: is the navigator item passed to your Dates component from its parent?
return (
<Dates
navigator={navigator}
... />
Probably inside a renderscene function where you render your navigator..
What does your output look like from your console statement?
console.log(eve, this.props)
I ran into this issue today, the reason is that you need to call the screen where you're using this.props.navigator.push with the NavigatorIOS component. That will set the navigator prop. E.g.
<NavigatorIOS
style={styles.container}
initialRoute={{
title: '',
component: DemoScreen
}}
/>
Now in your DemoScreen you can use this.props.navigator.