Error: Undefined is not a function - React Native - android

I created a simple activity where in if you press inside the circular area, the text in it should change accordingly. The app runs well but when I press inside the circular area, I get an error saying "undefined is not a function ( evaluating 'this.setState( {pressing: true});' ) ".
Also, the text inside the circular area should be initially set but it is empty.
You can see the activity here. The code is also provided below:
import React, { Component } from "react";
import {
StyleSheet,
View,
AppRegistry,
Text,
TouchableHighlight
} from "react-native";
class dhrumil extends Component {
constructor(props) {
super(props);
this.state = { pressing: false };
}
_inPress() {
this.setState({ pressing: true });
}
_outPress() {
this.setState({ pressing: false });
}
render() {
return (
<View style={styles.mainContainer}>
<TouchableHighlight
onPressIn={this._inPress}
onPressOut={this._outPress}
style={styles.toucher}
>
<View style={styles.smallContainer}>
<Text style={styles.texter}>
{this.state.pressing ? "EEK" : "PUSH ME"}
</Text>
</View>
</TouchableHighlight>
</View>
);
}
}
var styles = StyleSheet.create({
mainContainer: {
flex: 1,
backgroundColor: "white",
justifyContent: "center",
margin: 10,
alignItems: "center"
},
toucher: {
borderRadius: 100
},
smallContainer: {
backgroundColor: "#ff0000",
width: 200,
height: 200,
borderRadius: 100
},
texter: {
color: "white",
fontSize: 10,
fontWeight: "bold"
}
});
AppRegistry.registerComponent("dhrumil", () => dhrumil);
How can I solve this?

The issue is here:
<TouchableHighlight onPressIn={this._inPress}
onPressOut={this._outPress} style={styles.toucher}>
You are setting handlers without fixing the context as the current this.
So when the functions are called setState is not found as this will be different. Use bind.
<TouchableHighlight onPressIn={this._inPress.bind(this)}
onPressOut={this._outPress.bind(this)} style={styles.toucher}>
Or you can also use arrow function:
<TouchableHighlight onPressIn={() => this._inPress()}
onPressOut={() => this._outPress()} style={styles.toucher}>

simply delete node_modules
run npm install
react-native run-android
It's worked for me.

Related

Flickering components when animating the maxHeight of the ScrollView (react-native)

I have To-Do list elements which can expand and collapse by pressing the associated button.
By pressing on the EXPAND Button the height of the Animated ScrollView gets adjusted. From 0 to 100 when expanding and from 100 to 0 when collapsing. When we expand two list-objects at the same time, the screen begins to flicker.
Here the code of one single todo-element (it is abbreviated, means the DONE button is not in it):
import React, { useState, useRef, memo } from 'react';
import { Animated, Text, View, Button, ScrollView } from 'react-native';
import longText from '../data/data';
const ListObject = (props) => {
//Object Expand and Collapse feature
const expandValue = useRef(new Animated.Value(0)).current;
const [expandState, setExpand] = useState(false);
const expandAnimation = () => {
Animated.timing(expandValue, {toValue: 100, duration: 1000, useNativeDriver: false}).start();
setExpand(true);
}
const collapseAnimation = () => {
Animated.timing(expandValue, {toValue: 0, duration: 1000, useNativeDriver: false}).start();
setExpand(false);
}
return (
<View style={{ margin: props.margin }}>
<View style={{
flexDirection: 'row',
backgroundColor: 'grey',
borderRadius: 10,
}}>
<Button title='EXPAND' style={{
flex: 1,
backgroundColor: 'blue',
}}
onPress={ expandState ? collapseAnimation : expandAnimation }
/>
</View>
<Animated.ScrollView style={{
flex: 1,
paddingHorizontal: 40,
backgroundColor: 'grey',
borderRadius: 10,
maxHeight: expandValue
}}>
<Text>{ props.text }</Text>
</Animated.ScrollView>
</View>
);
}
export default memo(ListObject);
Here is the code for the App. To make a collection of all todo-elements, I map over a list and assign a key to each element:
mport React, { useRef, useState } from 'react';
import { Animated, StyleSheet, ScrollView, Text, View, SafeAreaView, Button } from 'react-native';
import longText from './src/data/data';
import ListObject from './src/components/list-object'
const styles = StyleSheet.create({
safeContainer: {
flex: 1.2
},
headerContainer: {
flex: 0.2,
flexDirection: 'column',
justifyContent: 'center',
backgroundColor: 'lightblue',
},
headerFont: {
fontSize: 50,
textAlign: 'center',
},
scrollContainer: {
flex: 1
}
});
const App = () => {
const numbers = [1,2,3,4,5,6,7,8,9];
const listItems = numbers.map((number) =>
<ListObject key={number.toString()} margin={10} headerText='I am the header of the to-do element' text={longText} />
)
return (
<SafeAreaView style={ styles.safeContainer } >
<View style={ styles.headerContainer }>
<Text style={ [styles.headerFont] }>LIST MAKER</Text>
</View>
<ScrollView style={ styles.scrollContainer }>
{listItems}
</ScrollView>
</SafeAreaView>
);
};
export default App;
I expected no flickering. The flickering appears also on my physical Android device. I have searched for similar problems and checked other libraries how they implement it.
For this, you can use react-native-collapsible
import Accordion from 'react-native-collapsible/Accordion';
const [activeSections, setActiveSessions] = useState([])
const _updateSections = (activeSections) => {
setActiveSessions(activeSections.includes(undefined) ? [] : activeSections)
}
<Accordion
sections={data}
activeSections={activeSections}
duration={400}
renderHeader={_renderHeader}
renderContent={_renderContent}
onChange={_updateSections}
touchableComponent={TouchableOpacity}
renderAsFlatList={true}
expandMultiple={true}
/>
For better performance and a smooth experience use this one.
I found the mistake by myself, it's a beginner's mistake.
Instead of managing the state of the component in the component itself, I had to lift the state up to the parent.
Here the link to the ReactJS learning doc.

How can I add a background image to my react native app?

I am fairly new to react native, but I have some experience. I am creating my own app for both ios and android by following along a tutorial that I had already completed and making my own modifications. As I was in the middle of creating an app, I forgot to add a background image. After struggling with this for several days, I'm desperate, so I decided to ask my question on here.
I am trying to add the background image to my App.js file. The image loads properly, but my page content ( which is inside <LifelineNavigator />) is either hidden or has disappeared for android. And for ios, the content seems to be in a small centered flexbox. Also, I am trying to remove the white background.
Any suggestions would definitely help. Thanks so much! God bless!
Here is my code:
import React, { useState } from "react";
import { StyleSheet, View, ImageBackground } from "react-native";
import * as Font from "expo-font";
import AppLoading from "expo-app-loading";
import { enableScreens } from "react-native-screens";
import LifelineNavigator from "./src/navigation/LifelineNavigator";
enableScreens();
const fetchFonts = () => {
return Font.loadAsync({
"Gotham-Medium": require("./src/assets/fonts/Gotham-Font/Gotham-Medium.otf")
const image = {
uri: "https://i.ibb.co/8z6QbTS/Lifeline-Gradient-Background-24.png",
};
const App = () => {
const [fontLoaded, setFontLoaded] = useState(false);
if (!fontLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => setFontLoaded(true)}
onError={(err) => console.log(err)}
/>
);
}
return (
<View >
<ImageBackground source={image} style={styles.image}>
<View style={ styles.container }>
<LifelineNavigator />
</View>
</ImageBackground>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
// backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
image: {
width: "100%",
height: "100%" ,
}
});
export default App;
IOS Screenshot
Android Screenshot
import React from "react";
import { ImageBackground, StyleSheet, Text, View } from "react-native";
const image = { uri: "https://reactjs.org/logo-og.png" };
const App = () => (
<View style={styles.container}>
<ImageBackground source={image} resizeMode="cover" style={styles.image}>
<Text style={styles.text}>Inside</Text>
</ImageBackground>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
},
image: {
flex: 1,
justifyContent: "center"
},
text: {
color: "white",
fontSize: 42,
lineHeight: 84,
fontWeight: "bold",
textAlign: "center",
backgroundColor: "#000000c0"
}
});
export default App;
follow this documentation > https://reactnative.dev/docs/imagebackground
it may solve your problem

React Native - Margin or Padding Not Working on Android Emulator

Below is the code for my component. Problem is that I cannot add any space in between the buttons, only on Android emulator as you can see in the image. Right now you see marginBottom but if I replace it with padding, it still does not work.
Appreciate if anyone has an idea why is this happening.
import React from 'react';
import { StyleSheet, View} from 'react-native';
import { Button } from 'react-native-elements';
const HomeScreen = ({navigation}) => {
return(
<View style={styles.container}>
<View>
<Button title="Sign In" onPress={() => navigation.navigate('SignIn')} style={{ marginBottom: 20}}/>
<Button title="Sign Up" onPress={() => navigation.navigate('SignUp')} />
</View>
</View>
)
}
HomeScreen.navigationOptions = () => {
return {
header: () => false
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingLeft: 20,
paddingRight: 20,
paddingTop: 40,
backgroundColor: 'white'
}
});
export default HomeScreen;
As I can understand you are using react-native-elements, and style prop is not vailable on that component, I can see on the documentation try using this prop "buttonStyle" to style the button and if not possible wrap the button around a view,
Thanks

Open another screen in react-native

I have this screen in react native
import React, { Component } from 'react';
import { AppRegistry,TouchableOpacity, Text ,Button,Image,TextInput,PropTypes,StyleSheet,View,NavigatorIOS,TouchableHighlight} from 'react-native';
class LoginView extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>
HYGEX
</Text>
<View>
<TextInput
placeholder="Username"
style={styles.formInput}
/>
<TextInput
placeholder="Password"
secureTextEntry={true}
style={styles.formInput1}
/>
<TouchableHighlight style={styles.button}
onPress={() => this.move()}>
<Text style={styles.buttonText}>Login</Text>
</TouchableHighlight>
</View>
</View>
);
}
move() {
//what i can do here to go to Socrce screen ???
}
}
Something like login screen, now when I click into TouchableHighlight
I need to open this screen
'use strict';
import React, { Component } from 'react';
import { AppRegistry, ListView, Text, View } from 'react-native';
class HygexListView extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
'John', 'Joel', 'James', 'Jimmy', 'Jackson', 'Jillian', 'Julie', 'Devin'
])
};
}
render() {
return (
<View style={{flex: 1, paddingTop: 22}}>
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>{rowData}</Text>}
/>
</View>
);
}
}
module.exports = HygexListView;
I tried to implement move method but I failed! Any idea why?
Does react-native have a method to change the screen when click into TouchableHighlight?
As others pointed out, you have to use an instance of Navigator to transition between screens. Maybe you can have a look at the example apps in the React Native repo. I also find this router package quite easy to set up, and it also includes an example app that you can use as a starting point.
Edit
As a simple example using react-native-router-flux, you can edit the Example.js file in the Example directory to look like this:
import React, {
Component,
} from 'react';
import {
StyleSheet,
Text,
View,
} from 'react-native';
import LoginView from './LoginView';
import HygexListView from './HygexListView';
import {
Scene,
Router,
Actions,
} from 'react-native-router-flux';
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'transparent', justifyContent: 'center',
alignItems: 'center',
},
tabBarStyle: {
backgroundColor: '#eee',
},
tabBarSelectedItemStyle: {
backgroundColor: '#ddd',
},
});
// define this based on the styles/dimensions you use
const getSceneStyle = (/* NavigationSceneRendererProps */ props, computedProps) => {
const style = {
flex: 1,
backgroundColor: '#fff',
shadowColor: null,
shadowOffset: null,
shadowOpacity: null,
shadowRadius: null,
};
if (computedProps.isActive) {
style.marginTop = computedProps.hideNavBar ? 0 : 64;
style.marginBottom = computedProps.hideTabBar ? 0 : 50;
}
return style;
};
class Example extends Component {
render() {
return (
<Router getSceneStyle={getSceneStyle}>
<Scene key="login" component={LoginView} initial={true}/>
<Scene key="hygex" component={HygexListView } />
</Router>
);
}
}
export default Example;
Then, in your component's move function, you have to do the following:
move(){
Actions.hygex(); // This will perform a slide transition, but you can customize it. Have a look at the docs for that.
I have not tested the code, so there might be some typos/missing imports/code, but it should give you an idea of what you have to do.
}
You have to implement a Navigator, which is roughly a component that manages all stuff related to screens, and header bar with back button and etc.
As you are a beginner, I suggest you to look at the docs on this link:
https://facebook.github.io/react-native/docs/navigator.html
Sorry for the short answer, I'm on my phone.
Good luck!
"use strict";
var React = require("react-native");
var {
Component,
StyleSheet,
Text,
TextInput,
TouchableHighlight,
View,
} = React;
var SecureView = require("./SecureView");
class LoginView extends Component {
constructor(props) {
super(props);
this.state = {
username: "",
password: ""
};
}
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>
Sign In
</Text>
<View>
<TextInput
placeholder="Username"
onChange={(event) => this.setState({username: event.nativeEvent.text})}
style={styles.formInput}
value={this.state.username} />
<TextInput
placeholder="Password"
secureTextEntry={true}
onChange={(event) => this.setState({password: event.nativeEvent.text})}
style={styles.formInput}
value={this.state.password} />
<TouchableHighlight onPress={(this.onSubmitPressed.bind(this))} style={styles.button}>
<Text style={styles.buttonText}>Submit</Text>
</TouchableHighlight>
</View>
</View>
);
}
onSubmitPressed() {
this.props.navigator.push({
title: "Secure Page",
component: SecureView,
passProps: {username: this.state.username, password: this.state.password},
});
}
};
var styles = StyleSheet.create({
container: {
padding: 30,
marginTop: 65,
alignItems: "stretch"
},
title: {
fontSize: 18,
marginBottom: 10
},
formInput: {
height: 36,
padding: 10,
marginRight: 5,
marginBottom: 5,
marginTop: 5,
flex: 1,
fontSize: 18,
borderWidth: 1,
borderColor: "#555555",
borderRadius: 8,
color: "#555555"
},
button: {
height: 36,
flex: 1,
backgroundColor: "#555555",
borderColor: "#555555",
borderWidth: 1,
borderRadius: 8,
marginTop: 10,
justifyContent: "center"
},
buttonText: {
fontSize: 18,
color: "#ffffff",
alignSelf: "center"
},
});
module.exports = LoginView;
You have to use navigator. please read the documentation as mentioned below. and if you will need then i will share you my code.
Here is an example: NavigatorExample

Best way to create a grid of TouchableOpacity items with different callbacks?

I have an app where I want to show a list of images as a grid with the first element on the list being an "add"-button. I also need every image to be Touchable. Currently my code looks like this:
class RNtest extends Component {
constructor() {
super();
this.state = {
data: [],
counter: 0 //Used to give unique numbers to the items
};
}
itemPicker() {
var temp = this.state.data;
temp.push({text: this.state.counter});
this.setState({
data: temp,
counter: this.state.counter + 1
});
}
onPress() {
console.warn('YO!');
}
render() {
return (
<ScrollView style={styles.body}>
<View style={styles.section}>
<Text style={styles.sectionHeader}>ITEMS</Text>
<View style={styles.gallery}>
<TouchableOpacity
style={styles.addButton}
onPress={this.itemPicker.bind(this)}
>
<Text>ADD</Text>
</TouchableOpacity>
{this.state.data.map(function(item) {
return (
<TouchableOpacity
style={styles.item}
onPress={this.onPress.bind(this)}
>
<Text>{item.text}</Text>
</TouchableOpacity>
);
})}
</View>
</View>
</ScrollView>
);
}
}
The stylesheet in case someone needs it:
var styles = StyleSheet.create ({
addButton: {
alignItems: 'center',
justifyContent: 'center',
height: 72,
width: 72,
borderRadius: 2,
marginVertical: 6,
marginRight: 6,
backgroundColor: 'white'
},
gallery: {
flexDirection: 'row',
flexWrap: 'wrap'
},
item: {
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#DCFFC2',
height: 72,
width: 72,
borderRadius: 2,
margin: 6
},
section: {
flexDirection: 'column',
paddingTop: 10
},
sectionHeader: {
fontSize: 13,
fontWeight: '400',
paddingBottom: 1,
color: '#A7A7A7'
},
body: {
flex: 1,
backgroundColor: '#EEEEEE',
paddingVertical: 10,
paddingHorizontal: 20
},
});
Obviously the onPress function doesn't work because apparently this is not visible inside the for-loop.
I could use ListView but then inside renderRow I would have to check if the item to be rendered is the add button, and it would make a few other parts more difficult as well.
Could I use refs in this situation, and is it a good idea?
I'm running React Native 0.27.2 on Android emulator.
If the only the problem you're having at the moment is scope-related, this can best be corrected by specifying a scope for your map function that processes your this.state.data. However I see a couple of other issues to comment on with your code.
Your itemPicker function is directly mutating your state via the push function. Opt for using a function such as concat which will return a new array (or the spread operator). Refer to the docs for reasons why you shouldn't directly mutate state like this.
Make sure you add a unique key to the resulting TouchableOpacity items you're creating (see warnings from React)
I've modified your code sample to demonstrate these concepts:
import React, { Component } from 'react';
import {
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View
} from 'react-native';
class RNTest extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
counter: 0
};
}
itemPicker() {
// 'push' will mutate, concat will return a new array
this.setState({
data: this.state.data.concat({text: this.state.counter}),
counter: this.state.counter + 1
});
}
createButton (item, index) {
// add a unique key, per react warnings
return (
<TouchableOpacity
key={index}
style={styles.item}
onPress={this.onPress}>
<Text>{item.text}</Text>
</TouchableOpacity>
);
}
onPress() {
console.warn('YO!');
}
render() {
return (
<ScrollView style={styles.body}>
<View style={styles.section}>
<Text style={styles.sectionHeader}>ITEMS</Text>
<View style={styles.gallery}>
<TouchableOpacity
style={styles.addButton}
onPress={this.itemPicker.bind(this)}
>
<Text>ADD</Text>
</TouchableOpacity>
{this.state.data.map(this.createButton, this)}
</View>
</View>
</ScrollView>
);
}
}
export default RNTest;
Try using arrow function:
{this.state.data.map((item) => {
// your code
})}
Or you could use bind:
{this.state.data.map(function(item) {
return (
<TouchableOpacity
style={styles.item}
onPress={this.onPress.bind(this)}
>
<Text>{item.text}</Text>
</TouchableOpacity>
);
}.bind(this))}
What both solutions do is passing the this parameter to inside your loop.

Categories

Resources