How to solve this? I have a custom Button component that gets included in my ModalScreen. It all seems to work but the onPress function is acting up. It seems to have lost the scope for some reason so I can't reference this or even add params to the function. What am I missing here?
import React from 'react';
import {
Text,
View,
} from 'react-native';
import Styles from 'app/constants/Styles';
import Button from 'app/components/Button';
export default class ModalScreen extends React.Component {
onBtnPress() {
console.log('dfsdfsdf'); /// << this gets consoled out just fine
//this.props.navigation.navigate('Main'); <<<< this won't work
}
render() {
return (
<View style={Styles.center}>
<Text>MOdalScreen</Text>
<Button label='Gå vidare' onPress={ this.onBtnPress }/>
</View>
);
}
}
Button.js
import React, { Component } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import Styles from 'app/constants/Styles'
import Vars from 'app/constants/Vars'
export default class Button extends Component {
render() {
return (
<TouchableOpacity style={[Styles.round, Styles.primaryBackground]} onPress={this.props.onPress}>
<Text style={[Styles.label, Styles.textCenter, { margin: Vars.spacing.narrow }]}>
{this.props.label}
</Text>
</TouchableOpacity>
)
}
}
You're right that it's lost the scope. You just need to bind the scope to the handle function somehow before you give it to the Button component. One popular way is with a handy arrow function:
<Button label='Gå vidare' onPress={ () => this.onBtnPress() }/>
Or you could use bind in the constuctor:
export default class ModalScreen extends React.Component {
constructor(props) {
super(props);
this.onBtnPress = this.onBtnPress.bind(this)
}
onBtnPress() {
console.log('dfsdfsdf'); /// << this gets consoled out just fine
//this.props.navigation.navigate('Main'); <<<< this won't work
}
Related
I'm new in react native. as i checked my code is fine where i am learning from about component but still getting error for props even in documentation react native using props component as i am using but why still getting error check document link (https://facebook.github.io/react-native/docs/props)
import React, {Component} from 'react';
import { StyleSheet, Text, View } from 'react-native';
class UserInformation extends Component(){
render(){
return(
<View>
<Text>{this.props.Name}</Text>
<Text>{this.props.Status}</Text>
</View>
);
}
}
function App() {
return (
<View style={styles.container}>
<UserInformation Name='Hamza' Status='Single' />
<UserInformation Name='Shahwar' Status='Comitted' />
</View>
);
}
export default App;
Error: TypeError: Unable to set property 'props' of undefined or null reference
You can't extend a functional component. Instead seems to me it is a typo error here:
class UserInformation extends Component{
//--------^^-------remove ()
If you're using Class Component you need to change
class UserInformation extends Component(){
to
class UserInformation extends React.Component{
and add
constructor(props) {
super(props) // <---- this is the part which give you this.props
}
But ... I suggest you use function component like App()
const UserInformation = (props) => {
return(
<View>
<Text>{props.Name}</Text>
<Text>{props.Status}</Text>
</View>
);
}
I am a beginner in the react-native framework. I am building an sample mobile application. And I am stuck at the very basic step.
I am trying to call 'getBusinessNews()' function on click event, which will return a component. But due to some reason, it's not returning.
I have doubled checked the path of the component and its correct.
Even the console.log defined inside the function is getting displayed.
I am attaching the code, all kind of help is appreciated.
Thank you in advance.
import React, {Component} from 'react';
import {Text,View,TouchableOpacity} from 'react-native';
import BusinessNews from '../News/BusinessNews';
class categories extends Component{
render(){
return(
<View>
<TouchableOpacity onPress={this.getBusinessNews.bind(this)}>
<Text>Business News</Text>
</TouchableOpacity>
</View>
);
}
getBusinessNews(){
console.log(1);
return(
<BusinessNews />
);
}
export default categories;
Returning component from event handler/listener to render will not work.
Instead do state update to decide whether component BusinessNews will render or not.
Its should do like this.
constructor
constructor() {
this.state = {
showBusiness: false//initially set to false
}
}
getBusinessNews
getBusinessNews() {
this.setState({showBusiness: true})//set to true to show BusinessNews
}
render
render() {
render() {
return (
<View>
<TouchableOpacity
onPress={this
.getBusinessNews
.bind(this)}>
{this.state.showBusiness ===true && <BusinessNews/>}//check boolean true
<Text>Business News</Text>
</TouchableOpacity>
</View>
);
}
}
You can return a component on click. As you have to returns component in render() only. You need to change the logic to render component.
You can call <BusinessNews /> in somewhere in render() to add component.
I'm trying to wrap a button that already exists with the functionality of recordActivityTag. Right now the way it is working it is not capturing the function of the data from the first button. I need to follow tags throughout the app. That's why I've constructed recordActivityTag. That functionality works fine for events, except for buttons.
How do I call within the onPress - if that's possible - the action on a button from another screen? I know I need to pass in the outer prop into TouchableOpacity and then call the other button.
import * as React from "react";
import { Core } from "common";
import { TouchableOpacity } from "react-native";
interface BtnProps {
readonly data: string
readonly recordActivityTag:(tag: Core.ActivityTag) => void
}
export default class Button extends React.Component<BtnProps, {}> {
constructor(props: BtnProps) {
super(props);
}
render() {
const { recordActivityTag } = this.props;
return (
<TouchableOpacity>
onPress {() => recordActivityTag}
{this.props.children}
</TouchableOpacity>
);
}
}
Make sure that you give your function to the onPress prop of the TouchableOpacity component, and that you actually call recordActivityTag within it.
export default class Button extends React.Component<BtnProps, {}> {
render() {
const { recordActivityTag } = this.props;
return (
<TouchableOpacity onPress={() => recordActivityTag()}>
{this.props.children}
</TouchableOpacity>
);
}
}
I have a screen inside my react-navigation StackNavigator that looks like this:
import React from 'react';
import { FlatList, ScrollView, StyleSheet, Text, View } from 'react-native';
import { List, ListItem } from 'react-native-elements';
import Accordion from '#ercpereda/react-native-accordion';
export default class PassportScreen extends React.Component {
static navigationOptions = {
title: 'Passport Recovery',
};
constructor(props) {
super(props);
this.renderItem = this.renderItem.bind(this);
}
renderItem(item) {
return (
<View>
<Accordion
header={item.item.key}
content={item.item.content}
/>
</View>
)
}
render() {
const instructions = [
{
key: <Text>1. Fill out a passport application form</Text>,
content: <Text>Content</Text>
},
{
key: <Text>2. Fill out a lost/missing passport statement</Text>,
content: <Text>Content</Text>
},
///...etc
];
return (
<ScrollView>
<FlatList
data={instructions}
renderItem={this.renderItem}
/>
</ScrollView>
)
}
}
module.exports = PassportScreen;
however, when I click to navigate to this screen from another screen, I get this error: TypeError: this.props.header is not a function. (In 'this.props.header({
isOpen: this.state.is_visible
})', 'this.props.header' is an instance of Object).
Other questions I've looked at with similar errors have mentioned passing props to the constructor and needing to pass this.renderItem instead of this.renderItem(), both of which I have already done, so I'm wondering if the problem comes from the fact that this screen is inside a StackNavigator and is navigated to by clicking on a ListItem. Is my intuition correct? If so, how can I fix this?
It seems that the header prop accepts a function, rather than just a component like content does.
Right now you're directly passing an object to the header prop, therefore it won't accept the callback function.
You may try the following approach in order to pass a callback to the header.
PassportScreen.js
customFunc = (callback) => {
console.log(callback)
}
renderItem = (item) => { // Useful to bind `this`
return (
<View>
<Accordion
header={this.customFunc}
content={item.item.content}
/>
</View>
)
}
ChildComponent.js
this.props.header('I'm setting the callback here')
Ok, I realise this code is smelly but just bear with me for a moment.
I was playing around with the code here and I have a strange situation wrt the this keyword.
In the code snippets that follow below, I want a button press in TopBar to trigger the goose method in index.ios.js, which it does. However, inside goose this refers to the great grandchild:
Child = ComponentOne
Grandchild = VoterScreen
Great-Grandchild = TopBar
Clearly, this is due to the way I'm passing the props down to the TopBar which I'm certain isn't the 'React Native way' but can anybody tell me either:
1 - How to adapt the code below and refer to this correctly (from what I've read that would make render be called again because of a change in state, eliminating the need to call forceUpdate()).
OR:
2 - How to enable the great-grandchild to call a goose where this is the this I expect by editing the way the callbacks are working.
The code for index.ios.js:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
View
} from 'react-native';
import VoterScreen from './components/voter-screen.js'
export default class StyleAB extends Component {
constructor(props)
{
super(props);
this.state =
{
componentSelected: 'One',
}
}
changeComponent = (component) =>{
this.setState({componentSelected: component});
}
goose(){
console.log('State before: ' + JSON.stringify(this.state));
this.state = { componentSelected: 'Two', }
console.log('State after: ' + JSON.stringify(this.state));
this.forceUpdate();
}
renderComponent(component) {
if(component == 'One') {
return <ComponentOne changeComponent={this.changeComponent} goose={this.goose} />
} else if(component == 'Two') {
return <ComponentTwo changeComponent={this.changeComponent} />
} else if(component == 'Three') {
return <ComponentThree changeComponent={this.changeComponent} />
}
}
render() {
console.log('Render is called');
return (
<View style={styles.container}>
{this.renderComponent(this.state.componentSelected)}
</View>
);
}
}
class ComponentOne extends Component {
render() {
return (
<VoterScreen sendData={this.props.goose}/>
)
}
}
class ComponentTwo extends Component {
render() {
return (
<UploadScreen/>
)
}
}
class ComponentThree extends Component {
render() {
return (
<ResultsScreen/>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
});
AppRegistry.registerComponent('StyleAB', () => StyleAB);
The code for voter-screen.js:
import React, { Component } from 'react';
import { View } from 'react-native';
import { Button } from 'react-native-elements';
import TopBar from './top-bar.js'
class VoterScreen extends Component {
render() {
console.log('Render is called in VoterScreen');
let topIsA = true;
return (
<View style={{flex: 1}}>
<TopBar parentToggle={this.props.sendData}/>
</View>
);
}
}
export default VoterScreen
The code for top-bar.js:
import React, { Component } from 'react'
import { View } from 'react-native';
import { Button } from 'react-native-elements';
class TopBar extends Component {
render() {
console.log('Render is called in TopBar');
return (
<View style={{flex: 1, flexDirection: 'row', backgroundColor: 'black'}}>
<Button
raised
containerViewStyle={{backgroundColor: 'black'}}
icon={{name: 'camera', type: 'font-awesome', size: 20, style: {marginRight: 0, textAlign: 'center'}}}
buttonStyle={{backgroundColor: '#cd00cd', borderRadius: 10, width: 50, height: 50}}
onPress={this.props.parentToggle.bind(this)}
/>
</View>
)
}
}
export default TopBar
EDIT:
The answer was to do 3 things:
1 - In index.ios.js:
Change:
return <ComponentOne changeComponent={this.changeComponent} goose={this.goose} />
To:
return <ComponentOne changeComponent={this.changeComponent} goose={this.goose.bind(this)} />
2 - in the same file:
Change:
<VoterScreen sendData={this.props.goose}/>
To:
<VoterScreen sendData={this.props.goose.bind(this)}/> [Thanks Chris].
3 - In top-bar.js:
Change:
onPress={this.props.parentToggle.bind(this)}
To:
onPress={this.props.parentToggle} [Thanks Eduard].
Try binding the scope before passing it down like this...
<VoterScreen sendData={this.props.goose.bind(this)}/>
If you want this to refer to the parent component, why do you bind it in the great grandchild?
Have you tried going this way?
onPress={this.props.parentToggle}
or this:
onPress={() => this.props.parentToggle}
Not sure if I read what you're asking correctly but changing goose(){ to goose = () => { should fix all your issues. Also it's not good for performance to bind in render so you might want to create another method on each component you want to bind or bind in the constructor.