I am setting up a React Native App. In this App i am trying to include a reusable Top Navigation Component, which accepts an array of strings and then renders them appropriately. When i am trying to compare the array string to a given string, it throws the following error:
Unhandeld JS Exception: TypeError: undefined is not an object (evaluating 'this.state.choseTopNavigation'
Inserting a string to compare works just fine
Setting up state object in constructor
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, ScrollView, FlatList} from 'react-native';
import {Header, TopNavigationButton} from './components';
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
topNavigationOptions : [
{key: 'Quick Search'},
{key: 'Person Search'}],
chosenTopNavigation:
"Quick Search",
};
}
}
renderItem({item}){
if (item.key == this.state.chosenTopNavigation) {
return (
<TopNavigationButton>
{item.key}
</TopNavigationButton>
)
} else {
return (
<Text style = {styles.unchosenTextStyle}>
{item.key}
</Text>
)
}
}
render() {
return (
<View>
<Header>Header</Header>
<ScrollView horizontal = {true}>
<View style = {styles.navigationContainer}>
<TopNavigationButton>Quick Search</TopNavigationButton>
<Text style = {styles.unchosenTextStyle}>Person Search</Text>
</View>
</ScrollView>
<FlatList
horizontal
data={this.state.topNavigationOptions}
renderItem = {this.renderItem}
/>
</View>
);
}
}
Note This Works
renderItem({item} ){
if (item.key == "Quick Search") { //inserting a string directly works
return (
<TopNavigationButton>
{item.key}
</TopNavigationButton>
)
} else {
return (
<Text style = {styles.unchosenTextStyle}>
{item.key}
</Text>
)
}
}
Thanks a lot for your help!
Either you can make your function as arrow function like this:
renderItem = ({item}) => {
// your existing code
}
or you need to bind your function in constructor like this:
this.renderItem = this.renderItem.bind(this);
Related
I'm developing a component to publish it in npm, but I'd like to call my component using a method instead of a tag.
Example:
myComponent.js
import React from 'react'
import { View, Text } from 'react-native'
export const showComponent = () => {
// this would be the function that I user to call my component down
}
const myComponent = (props) => {
return(
<View>
<Text>Oi</Text>
</View>
)
}
App.js
import React from 'react'
import { View, Text, TouchableOpacity } from 'react-native'
import { showComponent } from 'my-component'
const App = () => {
return(
<View>
<TouchableOpacity onPress={() => showComponent()}>
<Text>Home</Text>
</TouchableOpacity>
</View>
)
}
export defaul App
the idea is that when calling the showComponent function I show my component, and when I call, for example, the hide function, I close my component.
You can do it using a single class export:
import * as React from 'react';
export default class MyComponent extends React.Component {
state = {
isOpen: false,
};
open = () => {
this.setState({ isOpen: true });
};
close = () => {
this.setState({ isOpen: true });
};
render() {
const { isOpen } = this.state;
return !isOpen ? null : (
<View>
<Text>Oi</Text>
</View>
);
}
}
And you use it like so:
<MyComponent ref={(x) => this.myComponent = x)} />
And you open it like so:
this.myComponent.open();
I see in a comment above you want to call the component with a redux action, so you should call your redux action in that on click, but the component you want to show/hide needs to be linked to a redux state variable. Then in your jsx you'd have:
<View>
<TouchableOpacity onPress={() => showComponent()}>
<Text>Home</Text>
</TouchableOpacity>
{reduxBoolean && <MyComponent />}
</View>
import React from 'react'
import { View, Text} from 'react-native'
const example = (props) => {
return (
<View>
<Text>Hello</Text>
</View>
)
}
// props
import React from 'react'
import { View, Text} from 'react-native'
const examples = () => {
return(
<View>
<Text><example/></Text>
</View>
)
}
and print is : Hello
I'm new to react native. I am trying to get a 'Key' from a different component. I mean I am trying to call a function from a different component, as a parent component. But, I'm totally jumbled with all these reference calls and all. Please suggest to me how to call a function from a different component.
// AddScreen.js
import React, { Component } from 'react';
import { AppRegistry, AsyncStorage, View, Text, Button, TextInput, StyleSheet, Image, TouchableHighlight, Linking } from 'react-native';
import styles from '../components/styles';
import { createStackNavigator } from 'react-navigation';
import History from '../components/History';
export default class AddScreen extends Component {
constructor(props) {
super(props);
this.state = {
myKey: '',
}
}
getKey = async () => {
try {
const value = await AsyncStorage.getItem('#MySuperStore:key');
this.setState({ myKey: value });
} catch (error) {
console.log("Error retrieving data" + error);
}
}
async saveKey(value) {
try {
await AsyncStorage.setItem('#MySuperStore:key', value);
} catch (error) {
console.log("Error saving data" + error);
}
}
componentDidMount() {
this.getKey();
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.MainContainer}>
<View style={styles.Date_input}>
<TextInput
placeholder="Add input"
value={this.state.myKey}
onChangeText={(value) => this.saveKey(value)}
/>
</View>
<View style={styles.getKeytext}>
<Text >
Stored key is = {this.state.myKey}
</Text>
</View>
<View style={styles.Historybutton}>
<Button
onPress={() => navigate('History')}
title="Press Me"
/>
</View>
</View>
)
}
}
//History.js
import React, { Component } from 'react';
import AddScreen from '../components/AddScreen';
import {
AppRegistry,
StyleSheet,
Text,
TextInput,
Button,
View,
AsyncStorage
} from 'react-native';
export default class History extends Component {
constructor(props) {
super(props);
this.state = {
myKey: ''
}
}
render() {call async function synchronously
return (
<View style={styles.container}>
<Button
style={styles.formButton}
onPress={this.onClick}
title="Get Key"
color="#2196f3"
accessibilityLabel="Get Key"
/>
<Text >
Stored key is = {this.state.mykey}
</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
padding: 30,
flex: 1,
backgroundColor: '#F5FCFF',
},
});
I just want to call the getKey function from the History component to get the myKey value on the History component's screen.
Please suggest to me, by taking my components as an example.
You just simply need to pass the key via navigation parameters.
<Button
onPress={() => navigate('History', { key: this.state.myKey })}
title="Press Me"
/>
and in your history component you can do
render() {
const key = this.props.navigation.getParam('key');
return (
// other code
)
}
You can read more about passing parameters here. https://reactnavigation.org/docs/en/params.html
I have created a Share icon which on click share's pdf or image file on social accounts like facebook, whatsapp, gmail, etc. I want to pass URL link of shareable file inside class component but getting error. If I hardcode the URL then it works fine but how can I pass URL which I receive from react navigation ?
Working code:
import React, { Component } from 'react';
import { Share, View, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
const shareOptions = {
title: 'Download Brochure',
url: 'https://cpbook.propstory.com/upload/project/brochure/5bc58ae6ca1cc858191327.pdf'
}
export default class Screen extends Component {
onSharePress = () => Share.share(shareOptions);
render() {
return (
<View>
<Text style={styles.infoTitle}>Share: </Text>
<TouchableOpacity onPress={this.onSharePress}>
<Icon style={{paddingLeft: 10, paddingTop: 10}}name="md-share" size={30} color="black"/>
</TouchableOpacity>
</View>
}
}
Above code works fine but below code gives error saying shareOptions is undefined. How can I overcome this problem ? I want to pass file URL inside shareOptions. I am getting file url from react navigation props i.e brochure.
Code:
import React, { Component } from 'react';
import { Share, View, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
export default class Screen extends Component {
onSharePress = () => Share.share(shareOptions); <---Getting error here shareOptions undefined
render() {
const project = this.props.navigation.getParam('project', null);
let { brochure } = project;
const shareOptions = {
title: 'Download Brochure',
url: brochure
}
return (
<View>
<Text style={styles.infoTitle}>Share: </Text>
<TouchableOpacity onPress={this.onSharePress}>
<Icon style={{paddingLeft: 10, paddingTop: 10}}name="md-share" size={30} color="black"/>
</TouchableOpacity>
</View>
)
}
How can I overcome above problem and how can I pass props i.e file URL in above case URL is in brochure props.
Just pass shareOptions object to onSharePress function as param and get shareOptions in onSharePress event handler function
import React, { Component } from 'react';
import { Share, View, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
export default class Screen extends Component {
onSharePress = (shareOptions) => {
Share.share(shareOptions);
}
render() {
const { navigation } = this.props;
const project = navigation.getParam('project', null);
const { brochure } = project;
const shareOptions = {
title: 'Download Brochure',
url: brochure
}
return (
<View>
<Text style={styles.infoTitle}>Share: </Text>
<TouchableOpacity onPress={() => this.onSharePress(shareOptions)}>
<Icon style={{paddingLeft: 10, paddingTop: 10}}name="md-share" size={30} color="black"/>
</TouchableOpacity>
</View>
)
}
}
You're not passing shareOptions to your share method, thus it is undefined.
There are many ways you could refactor your code, below you can see a more viable approach.
Since your render function used none of the logic above your return statement, I have simply migrated all of that into the onSharePress function, naturally if these differ, then you would want to pass it in via a parameter.
export default class Screen extends Component {
onSharePress = () => {
const project = this.props.navigation.getParam('project', null);
let { brochure } = project;
const shareOptions = {
title: 'Download Brochure',
url: brochure
}
Share.share(shareOptions);
}
render() {
return (
<View>
<Text style={styles.infoTitle}>Share: </Text>
<TouchableOpacity onPress={this.onSharePress>
<Icon style={{paddingLeft: 10, paddingTop: 10}}name="md-share" size={30} color="black"/>
</TouchableOpacity>
</View>
)
}
}
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 trying to use NavigatorIOS so in my index.ios.js I got:
'use strict';
var React = require('react-native');
var Home = require('./App/Components/Home');
var {
AppRegistry,
StyleSheet,
NavigatorIOS
} = React;
var styles = StyleSheet.create({
container:{
flex: 1,
backgroundColor: '#111111'
}
});
class ExampleApp extends React.Component{
render() {
return (
<NavigatorIOS
style={styles.container}
initialRoute={{
title: 'example',
component: Home
}} />
);
}
};
AppRegistry.registerComponent('exampleapp', () => ExampleApp);
module.exports = ExampleApp;
And then in the Home.js:
'use strict';
var React = require('react-native');
var Park = require('./Park');
var {
View,
StyleSheet,
Text,
TouchableHighlight
} = React;
var styles = StyleSheet.create({
...
});
class Home extends React.Component{
onPress() {
this.props.navigator.push({
title: 'Routed!',
component: Park
});
}
render() {
return (
<View style={styles.mainContainer}>
<Text> Testing React Native </Text>
<TouchableHighlight onPress={this.onPress} style={styles.button}>
<Text>Welcome to the NavigatorIOS . Press here!</Text>
</TouchableHighlight>
</View>
);
}
};
module.exports = Home;
The issue I have is that when I click on the TouchableHighlight triggering onPress(), I am getting an error:
"Error: undefined is not an object (evaluating 'this.props.navigator')
So it seems that it can't find the navigator from props but the navigator should be passed by NavigatorIOS?
Also it seems that the Home Component has this.props.navigator as per image:
Any hints?
I found a couple of links (people having exactly the same problem but that didn't help):
https://github.com/facebook/react-native/issues/416
undefined is not an object (evaluating 'this.props.navigator.push')
Since you're using ES6 you need to bind 'this'.
For example: onPress={this.onPress.bind(this)}
Edit: Yet another way that I've been using more recently is to use an arrow function on the function itself, since they will automatically bind the outside this.
onPress = () => {
this.props.navigator.push({
title: 'Routed!',
component: Park
});
};
You can bind the function to this in the constructor.
constructor(props) {
super(props);
this.onPress = this.onPress.bind(this);
}
Then use it when rendering without binding.
render() {
return (
<TouchableHighlight onPress={this.onPress} style={styles.button}>
<Text>Welcome to the NavigatorIOS. Press here!</Text>
</TouchableHighlight>
);
}
This has the advantage of being able to be used multiple times and also clean's up your render method.