I am currently learning React Native.
I just a built a very simple app to test out the Button component.
When I click on the button component the console log is printed as expected.
But after printing out the console log it pops out the following error.
**undefined is not an object (evaluating '_this2.btnPress().bind')**
I am not sure what is wrong ?
Can anyone let me know what I am doing wrong ?
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
export default class App extends React.Component {
btnPress() {
console.log("Fn Button pressed");
}
render() {
return (
<View style={styles.container}>
<Button title="this is a test"
onPress={()=> this.btnPress().bind(this)} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
You are invoking the function instead of passing a reference through bind.
loose the ().
And you should not wrap it with an arrow function as bind is already returning a new function instance
onPress={this.btnPress.bind(this)} />
By the way, this will return and create a function instance on each render, you should do it once in the constructor (which runs only once):
export default class App extends React.Component {
constructor(props){
super(props);
this.btnPress = this.btnPress.bind(this);
}
btnPress() {
console.log("Fn Button pressed");
}
render() {
return (
<View style={styles.container}>
<Button title="this is a test"
onPress={this.btnPress} />
</View>
);
}
}
Or use an arrow function which uses a lexical context for this:
export default class App extends React.Component {
btnPress = () => {
console.log("Fn Button pressed");
}
render() {
return (
<View style={styles.container}>
<Button title="this is a test"
onPress={this.btnPress} />
</View>
);
}
}
Related
I am not using class, I'd like to learn how to do it manually. I am dealing with login screen.
https://snack.expo.io/#ericsia/call-function-from-child-component
If you want to show me your code, you need to save and share the link. So I wanted a functional component for display textbox (assume ChildComponent as the function name, so export ChildComponent).
So in Parent/Screen1 I have something like this right?
import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import ChildComponent from './components/ChildComponent';
export default function App() {
function checkSuccess()
{
// call helloWorld from here
}
return (
<View style={styles.container}>
<ChildComponent />
<TouchableOpacity style={styles.button}
onPress={ checkSuccess } >
<Text>helloWorld ChildComponent</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
button: {
alignItems: "center",
backgroundColor: "#DDDDDD",
padding: 10
},
});
so if the result is invalid right, I wanted to display a tiny red error message.
something like this
My approach is if I can call a function from the ChildComponent then I may still solve it.
I googled it and most of the solution provided is for class.
I tried useEffect React.createRef useImperativeHandle but I didn't get it work.
for start, i am just trying to call the helloWorld()
import * as React from 'react';
import { TextInput , View, StyleSheet, Image } from 'react-native';
export default function ChildComponent() {
function helloWorld()
{
alert("Hello World");
}
return (<TextInput placeholder="Try to call helloWorld from App.js"/>);
}
Another question, if I have a textbox in my ChildComponent how do I retrieve the text/value from parent?
The Easy Way: Passing Props
You can move the helloWorld function up to the parent component and pass it down to the child as a prop. That way both components can call it. I recommend using an arrow function when you are going to be passing around a function, though it doesn't matter in this case.
Parent
export default function App() {
const helloWorld = () => {
alert('Hello World');
}
const checkSuccess = () => {
helloWorld();
}
return (
<View style={styles.container}>
<ChildComponent helloWorld={helloWorld} />
<TouchableOpacity style={styles.button} onPress={checkSuccess}>
<Text>helloWorld ChildComponent</Text>
</TouchableOpacity>
</View>
);
}
Child
const ChildComponent = ({ helloWorld }) => {
// could do anything with helloWorld here
return <TextInput placeholder="Try to call helloWorld from App.js" />;
};
The Hard Way: Ref Forwarding
If you want to keep the function in the child component then you need to go through a lot of hoops. I do not recommend this approach.
You have to do all of these steps:
Create a ref object in the parent using useRef: const childRef = React.useRef();
Pass the ref to the child as a prop: <ChildComponent ref={childRef} />
Call the function on the current value of the child component ref, using ?. to avoid errors if .current has not yet been set: childRef.current?.helloWorld();
Accept the ref prop in the child by using forwardRef: React.forwardRef( (props, ref) => {
Expose the helloWorld function as an instance variable of the child component by using useImperativeHandle: React.useImperativeHandle(ref , () => ({helloWorld}));
Parent:
export default function App() {
const childRef = React.useRef();
function checkSuccess() {
childRef.current?.helloWorld();
}
return (
<View style={styles.container}>
<ChildComponent ref={childRef} />
<TouchableOpacity style={styles.button} onPress={checkSuccess}>
<Text>helloWorld ChildComponent</Text>
</TouchableOpacity>
</View>
);
}
Child:
const ChildComponent = React.forwardRef((props, ref) => {
function helloWorld() {
alert('Hello World');
}
React.useImperativeHandle(ref, () => ({ helloWorld }));
return <TextInput placeholder="Try to call helloWorld from App.js" />;
});
Edit: Expo Link
First thing is that in Test1.js when i uses Test2.js two time.In the first one I passes the data="First" and in second one I passes data="Second". But when the screen render and when I press the Button First It show "First" on the screen but when I press button second,it do'nt render "Second" on screen.
As I found that the value of state in the Test2.js is not changing.
Can You explain me why the value of state is not changing in the Test2.js
And can anyone tell how to fix this.And more thing if I uses component instead of this.A() and this.B() it works fine but as below method not works.
Here is the code..
Test1.js
import React, { Component } from 'react'
import { Text, StyleSheet, View } from 'react-native'
import {Button} from 'react-native-elements'
import Test2 from './Test2';
var a = "First"
var b = "second"
export default class Test1 extends Component {
state={
activeA:true,
activeB:false
}
A = ()=>{
return( <Test2 data={a}/>)
}
B = () =>{
return(<Test2 data={b}/>)
}
render() {
return (
<View style={{ flex:1,justifyContent:'center',padding:20}}>
<Button buttonStyle={{ marginBottom: 10}} title="First" onPress={()=>{this.setState({activeA:true,activeB:false})}}/>
<Button buttonStyle={{ marginBottom: 10}} title="Second" onPress={()=>{this.setState({activeA:false,activeB:true,})}}/>
{this.state.activeA?this.A():this.B()}
</View>
)
}
}
Test2.js
import React, { Component } from 'react'
import { Text, View } from 'react-native'
export default class Test2 extends Component {
state={
data:this.props.data
}
render() {
return (
<View>
<Text syle = {{fontSize:20,}}> {this.state.data} </Text>
</View>
)
}
}
Do not transfer to the status value, but render directly.
Test2.js
import React, { Component } from 'react'
import { Text, View } from 'react-native'
export default class Test2 extends Component {
constructor(props) {
super(props);
const { data } = props
}
render() {
return (
<View>
<Text syle = {{fontSize:20,}}> {this.props.data} </Text>
</View>
)
}
}
Test2.propTypes = {
data: PropTypes.string
}
I am fairly new to React Native and I have problem with using onChangeText.
I am trying to have a TextInput to type in a word and updating a state. When I use onChangeText I can only type 1 symbol at a time until it re-renders. I can keep the value by using
value = {this.state.text} but the input field still lose focus everytime I write a letter.
I have also tried using onBlur and onSubmitEditing with no success.
This is my current code. Which is inside render().
<View style={{padding: 10}}>
<TextInput
onChangeText={(text) => { this.setState({text: text})} }
/>
<TouchableHighlight style={styles.button} onPress={this.handlePress.bind(this)}>
<Text style={styles.buttonText}>Login</Text>
</TouchableHighlight>
<Text style={{padding: 10, fontSize: 42}}>
{this.state.text}
</Text>
</View>
So by using this method I can currently only write one letter at a time as this.state.text will only consist of one letter at a time.
Any help appreciated.
Example
SOLVED
I used react-native-tab-view which uses it's own router.
I wrote my code as this
And as you see the rendering part happens outside of return(). That's what caused my problem.
I've removed react-native-tab-view and rewritten it like this
<TextInput style={styles.input}
placeholder='username'
onChangeText={(text) => { this.setState({ username: text})}}>
</TextInput>
You need { } to open and close the function block, else it return the setState
() => callFn is equivalent with () => {return callFn} so you return your setState call.
You need here () => {callFn}
And remove the {this.state.text} from your <Text> tag, that will trigger rerender every time you change the state
Try with this full component hope so this helpfull for u.
'use strict';
import React, { Component } from "react";
import { Text, View, TextInput } from 'react-native';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
text:''
};
}
render() {
let {text}=this.state;
return (
<View style={{padding: 10}}>
<TextInput onChangeText={(text) => { this.setState({ text: text})}}/>
<Text style={{padding: 10, fontSize: 42}}>
{text}
</Text>
</View>
)
}
}
export default Home;
It is not best practice to create functions within component props. This will always force a re-render even if nothing was changed due to the fact that the prop value is a new function.
Try it like this.
I also gave you a way to have multiple text inputs without creating a single inline function by use of "currying", along with making them into controlled inputs whereby their value is "controlled" by the state. Socialism in React!
'use strict';
import React, { Component } from "react";
import { Text, View, TextInput, StyleSheet } from 'react-native';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
name:''
email:''
nameError:''
emailError:''
};
}
onChangeText = name => text => this.setState({ [name]: text });
render() {
let { name, email, nameError, emailError } = this.state;
return (
<View style={styles.container}>
<TextInput onChangeText={this._onChangeText("name")} value={name} />
<Text style={styles.text}>{nameError}</Text>
<TextInput onChangeText={this._onChangeText("email"} value={email} />
<Text style={styles.text}>{emailError}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
text: {
padding: 10,
fontSize: 42
},
container: {
padding: 10
}
});
export default Home;
guy's I'm using the react-native-animatable library. Basically, when I load my app the animation runs, however, when I go to another tab and return to the initial page the animation doesn't run anymore. I think it's because it' doesn't get reloaded anymore and I was wondering how to reload a component. As you can see the View has an animation prop which is the animation which has to be loaded.
import React, { Component } from 'react';
import { Text, Button, StyleSheet, Image, ImageBackground, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import LinearGradient from 'react-native-linear-gradient';
import {Fonts} from '../components/Fonts';
import { createAnimatableComponent, View, } from 'react-native-animatable';
class Home extends React.Component {
render() {
return (
<View style={styles.container}>
<View animation='bounceInLeft'
style={styles.container1}>
<View style={styles.card1}>
<ImageBackground
source={require('../images/pictures/runop.jpg')}
style={{width:'100%', height:200, }}>
<Text
style={{fontSize:30, alignSelf:'center', color:'white',
fontFamily:Fonts.Nunito}}
> Sport Schema's</Text>
</ImageBackground>
</View>
</View>
<View animation='bounceInRight' style={styles.container2}>
<View style={styles.card2}>
<Image
source={require('../images/pictures/foodop.jpg')}
style={{width:'100%', height:200}}/>
</View>
</View>
<View animation='bounceInLeft' style={styles.container3}>
<View style={styles.card3}>
<Image
source={require('../images/pictures/blogop.jpg')}
style={{width:'100%', height:200}}/>
</View>
</View>
</View>
);
}
}
Thanks for your help. I eventually got it to work with a different method.
I used the withNavigationFocus from react-navigation to get the isFocused props of the current screen. Then i just used an if statement if screen is focused then run animation else dont.
import {withNavigationFocus} from 'react-navigation';
class Profile extends React.Component {
constructor(props) {
super(props);
}
render()
// This checks if current screen is focused then run the animation otherwise dont.
{
if (this.props.isFocused && this.animation) {
this.animation.bounce(800)
}
return (
<View ref={(ref) => {this.animation = ref;}}
style={styles.container3}>
<View style={styles.card3}>
<Text> hii</Text>
</View>
</View>
);
}
}
});
export default withNavigationFocus(Profile); // <- dont forget this!!
If you are using react-navigation, below solution might work for you.
Create a function which would start the animation after some milliseconds and pass it to the next screen as params. Example,
SCREEN A
animateFunction() {
setTimeout(() => {
// start your animation
}, 100);
}
navigation.navigate(SCREEN_NAME, { startPrevScreenAnimation: animateFunction });
And in the next screen call that function when the component unmounts (componentWillUnmount()). Example,
SCREEN B
componentWillUnmount() {
this.props.navigation.state.params.startPrevScreenAnimation();
}
I said some milliseconds because you would want the animation to start once the screen transition is complete.
OR
Add a listener to your screen which fires an event when the screen is in focus.
if (this.props.navigation) {
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => { // Run your animation },
);
}
I am trying to make an app where the SplashScreen is shown for 600 milliseconds and then the Mainscreen is displayed. I used the setTimeOut method in componentDidmount, but I am getting the error as shown here. The necessary code is also provided below.
import React, { Component } from 'react';
import {Image,Dimensions, Button, Text, StyleSheet, View, TouchableOpacity} from 'react-native';
var timePassed;
class SplashScreen extends Component{
constructor(props){
super(props);
this.state={
timePassed: false
}
}
componentDidMount() {
this.setTimeout( () => {
this.setState({timePassed: true})
},600);
}
render(){
if(!this.state.timePassed){
return(
<View style={styles.container}>
<Image style={styles.logo} source={require('./Images/logo.jpg')} resizeMode="contain" />
<Text style={styles.deadlineFont}>Deadline</Text>
</View>
);
}
else{
return (
<View>
<MainScreen />
</View>
);
}
}
}
Problem: setTimeout is wrongly called with this but is not a method defined on your class.
Solution: Change this.setTimeout( () => { to setTimeout( () => {.
Here's a working example of your code: https://repl.it/Iqfk/1