Call function outside in React Native - javascript

I have a React Native application and I need to call a function in HomeScreen definition to do something with it's elements. This action should be done by custom widget that is stored outside HomeScreen. I try to call the function from the widget's props, but it returns undefined. So, how can I propperly do this?
import React, { useState, useEffect, Component } from 'react';
import { Image, StyleSheet, Text, View, Button, Modal } from 'react-native';
import { LongPressGestureHandler } from 'react-native-gesture-handler';
export default function HomeScreen() {
const doSomething = () => {
console.log("Works");
}
return (
<View style={{padding: 30}}>
<LongPress
action={() => {doSomething()}}
>
<View>
<Text>Long press me</Text>
</View>
</LongPress>
</View>
);
};
function LongPress({children}, props) {
return (
<LongPressGestureHandler
onHandlerStateChange={() => {HomeScreen.action}}
minDurationMs={700}
>
<View>{children}</View>
</LongPressGestureHandler>
)
};

In your example you pass an action prop to your LongPress component. Assuming that's the function you need to fire, the following should suffice:
function LongPress({action, children}) {
return (
<LongPressGestureHandler
onHandlerStateChange={action}
minDurationMs={700}
>
<View>{children}</View>
</LongPressGestureHandler>
)
};

You can use PubSub.
class DownloadManager{
public static pointer;
async download() {
//download code
//when download update
PubSub.publish('download-complete', {status:true});
}
}
class BackupPage extends React.Component<any, any> {
componentDidMount() {
PubSub.subscribe('download-complete', (msg, data) => {
this.setState({ status :data.status})
})
}
render() {
//render code
}
}

Related

issues with clear Timeout at logout

I have a notification alert that shows after 60 seconds of login, the problem is that if someone logout within those 60 seconds. the message still show even if the user is no longer logged-in.
below is what I tried to do to solve this problem, but without success. do I need
I have tried default export to export timerId but since react-native only allows one default export and this happened before this part of the code I could not use default export.
I tried to do export timerId; but I got an error saying unresolved variable.
index.js:
_myAlert= () => {
timerId: setTimeout(()=>{
Alert.alert(
'Notification',
'Please set up your user account.'
);
}, 60000)
}
drawer.js:
onPress={() => {
clearTimeout(this.timerId);
this.jumpToSection('Logout');
}
}
Maybe, you could have a scope problem.
Next link could be help
https://cybmeta.com/var-let-y-const-en-javascript
Here is one solution where timerhandle is cleared only in componentWillUnmount but the alert is shown only if button is not pressed.
App.js
import React, { Component } from 'react';
import { View, Alert } from 'react-native';
import ClearButton from './src/ClearButton'
class App extends Component {
constructor(props) {
super(props);
this.timerHandle = 0;
this.state = {
pressed: false
}
}
componentDidMount() {
this.timerHandle = setTimeout(() => {
if (!this.state.pressed) {
Alert.alert(
'Notification',
'Please set up your user account.'
);
}
}, 60000);
}
componentWillUnmount() {
if (this.timerHandle) {
clearTimeout(this.timerHandle);
}
}
onPress() {
this.setState({ pressed: true });
}
render() {
return (
<View>
<ClearButton onPress={this.onPress.bind(this)}/>
</View>
);
}
}
export default App;
ClearButton.js
import React, { Component } from 'react';
import { View, TouchableOpacity } from 'react-native';
class ClearButton extends Component {
render() {
return (
<TouchableOpacity onPress={this.props.onPress.bind(this)}>
<View style={{width: 100, height: 100, backgroundColor: '#0000ff'}}/>
</TouchableOpacity>
);
}
}
export default ClearButton;
I guess you could also clear the timer in App.js onPress method if you wish not to use this.state.pressed but you should still not remove the clearing in componentWillUnmount.

Not rendering component onClick

The component I am trying to render:
import React, { Component } from 'react';
export default class QueryPrint extends Component {
render() {
console.log('working');
return (
<div>Hello</div>
)
}
}
The component that is trying to call it:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {
Button,
} from 'reactstrap';
import QueryPrint from './bq_print';
class QueryResults extends Component {
constructor(props) {
super(props);
this.print = this.print.bind(this);
}
print() {
console.log('Clicked');
return (
<QueryPrint />
);
}
render() {
return (
<Button
className='cuts-btn'
color='success'
onClick={this.print}
>
Print
</Button>
)
}
}
function mapStateToProps(state) {
return {
query_data: state.results.query_data
}
}
export default connect (mapStateToProps, null)(QueryResults);
The console.log('clicked') is working, but the component that is supposed to render in that method doesn't--no console.log('working') or <div>.
Returning something from a click callback has no effect. If you want to render something, you do so in the render method. The click callback's job is to call this.setState(), which will then kick off a render.
Perhaps something like this:
class QueryResults extends Component {
constructor(props) {
super(props);
this.print = this.print.bind(this);
this.state = {
queryPrint: false,
}
}
print() {
console.log('Clicked');
this.setState({ queryPrint: true })
}
render() {
const { queryPrint } = this.state;
return (
<React.Fragment>
{queryPrint && <QueryPrint />}
<Button
className='cuts-btn'
color='success'
onClick={this.print}
>
Print
</Button>
</React.Fragment>
)
}
}
React Native works differently. It is more like a web app - you need to navigate to the other component.
Look at this example its very to the point: https://facebook.github.io/react-native/docs/navigation
Alternatively if you want to make only part of the screen change you will need to include it into your own render and control it thru a flag or a state machine.
https://facebook.github.io/react-native/docs/direct-manipulation

React Native pass function to child component as prop

I'm new to React Native (and React), and I'm trying to pass a function as a prop to a component.
My goal is to create a component where its onPress functionality can be set by the instantiator of the component, so that it is more reusable.
Here is my code so far.
App.js
import React, { Component } from 'react';
import { View } from 'react-native';
import TouchableButton from './components/touchable-button';
export default class App extends Component<Props> {
constructor () {
super();
}
handlePress () {
// this should be called when my custom component is clicked
}
render () {
return (
<View>
<TouchableButton handlePress={this.handlePress.bind(this)}/>
</View>
);
}
}
TouchableButton.js
import React, { Component } from 'react';
import { TouchableHighlight } from 'react-native';
import AppButton from "./app-button";
export default class TouchableButton extends Component {
handlePress;
constructor(props){
super(props);
}
render () {
return (
<TouchableHighlight onPress={
this.props.handlePress
}>
<AppButton/>
</TouchableHighlight>
);
}
}
I am passing the handlePress function as the prop handlePress. I would expect the TouchableButton's props to contain that function, however it isn't there.
Solution
Use arrow function for no care about binding this.
And I recommend to check null before calling the props method.
App.js
export default class App extends Component<Props> {
constructor () {
super();
}
handlePress = () => {
// Do what you want.
}
render () {
return (
<View>
<TouchableButton onPress={this.handlePress}/>
</View>
);
}
}
TouchableButton.js
import React, { Component } from 'react';
import { TouchableHighlight } from 'react-native';
import AppButton from "./app-button";
export default class TouchableButton extends Component {
constructor(props){
super(props);
}
handlePress = () => {
// Need to check to prevent null exception.
this.props.onPress?.(); // Same as this.props.onPress && this.props.onPress();
}
render () {
return (
<TouchableHighlight onPress={this.handlePress}>
<AppButton/>
</TouchableHighlight>
);
}
}
When writing handlePress={this.handlePress.bind(this)} you passing a statement execution ( which when and if executed returns a function). What is expected is to pass the function itself either with handlePress={this.handlePress} (and do the binding in the constructor) or handlePress={() => this.handlePress()} which passes an anonymous function which when executed will execute handlePress in this class context.
// Parent
handleClick( name ){
alert(name);
}
<Child func={this.handleClick.bind(this)} />
// Children
let { func } = this.props;
func( 'VARIABLE' );

How to call a class inside an object that's inside the render function of a component in React Native?

I'm not sure how to describe what I'm trying to do with words so please take a look at the following code:
This is what causing me issues: this.fetchMessages()
import React, { Component } from 'react';
import { PushNotificationIOS, FlatList, TextInput, ActivityIndicator, ListView, Text, View, Image, TouchableWithoutFeedback, AsyncStorage } from 'react-native';
import { Actions } from 'react-native-router-flux';
import ConversationsItem from './ConversationsItem';
import { conversationFetch } from '../actions';
import { connect } from 'react-redux';
import { Divider } from 'react-native-elements'
import PushNotification from 'react-native-push-notification';
class Conversations extends Component {
componentDidMount() {
this.props.conversationFetch()
}
fetchMessages() {
this.props.conversationFetch()
}
render() {
PushNotification.configure({
onNotification: function(notification) {
PushNotification.getApplicationIconBadgeNumber((response) => {
PushNotification.setApplicationIconBadgeNumber(response + 1)
})
console.log( 'NOTIFICATION:', notification )
notification.finish(PushNotificationIOS.FetchResult.NoData);
this.fetchMessages()
}
});
if (!this.props.email) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
console.log(this.props.conversations)
return (
<View style={{flex: 1, backgroundColor: 'white'}}>
...
</View>
);
}
}
const mapStateToProps = (state) => {
console.log(state)
const { conversations } = state.conversation;
const { email } = state.conversation;
return { conversations, email };
};
export default connect(mapStateToProps, { conversationFetch })(Conversations);
When I call this.fetchMessages() inside PushNotification.configure({}), I get the following error message:
this.fetchMessages is not a function
I'm not sure if what I'm doing is possible but if so I'd really like to know how to make this work.
I tried adding .bind(this) and other ways around but got same error anyways.
Thanks for you help.
Functions declared with function keyword has their own this, so this inside onNotification function does not refer to the class.
Therefore use arrow function syntax, which will lexically resolve this and value of this inside will refer to class itself. So convert
onNotification: function(notification) {
to
onNotification: (notification) => {
So you have in fact tried binding the fetchMessages function in the constructor? Like such:
constructor(props) {
super(props)
this.fetchMessages = this.fetchMessages.bind(this);
}
You can also use an arrow function to bind your method to the class without calling the constructor like such:
() => this.fetchMessages()

Why am I getting an invalid value error?

I don't understand how I'm getting this error (pic below). In my LoginForm.js file, the onEmailChange(text) is giving me an unresolved function or method call to onEmailChange() error when I hover over it in my WebStorm IDE. In my index.js file, no error is being thrown anywhere.
I've looked around SO for this issue but it doesn't fully pertain to my problem.
I've tried File > Invalidate Caches/Restart but that didn't work.
Here's App.js:
import React, { Component } from 'react';
import {StyleSheet} from 'react-native';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import firebase from 'firebase';
import reducers from './reducers';
import LoginForm from './components/common/LoginForm';
class App extends Component {
render() {
return(
<Provider style={styles.c} store={createStore(reducers)}>
<LoginForm/>
</Provider>
);
}
}
const styles = StyleSheet.create({
c: {
flex: 1
}
});
export default App;
Here's LoginForm.js:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {emailChanged} from 'TorusTeensApp/src/actions';
import {Text, StyleSheet, KeyboardAvoidingView, TextInput, TouchableOpacity} from 'react-native';
class LoginForm extends Component {
render() {
onEmailChange(text)
{
this.props.emailChanged(text);
}
return(
<KeyboardAvoidingView style={styles.container}>
<TextInput
style={styles.userInput}
onsubmitediting={() => this.passwordInput.focus()}
returnKeyType={"next"}
placeholder={"Email"}
label={"Email"}
keyboardType={"email-address"}
autoCorrect={false}
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
/>
<TextInput
style={styles.userInput}
ref={(userInput) => this.passwordInput = userInput}
returnKeyType={"go"}
placeholder={"Password"}
label={"Password"}
secureTextEntry
/>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.buttonText}>Login</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.buttonText}>Create Account</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
);
}
}
const styles = StyleSheet.create({
container: {
padding: 20 // creates a gap from the bottom
},
userInput: {
marginBottom: 20,
backgroundColor: '#9b42f4',
height: 40
},
buttonContainer: {
backgroundColor: '#41bbf4',
paddingVertical: 10,
marginBottom: 20
},
buttonText: {
textAlign: 'center',
color: '#FFFFFF'
}
});
const mapStateToProps = state => {
return {
email: state.auth.email
};
};
export default connect(mapStateToProps, null, {emailChanged}) (LoginForm);
Here's index.js:
import {EMAIL_CHANGED} from './types';
export const emailChanged = (text) => {
return {
type: 'EMAIL_CHANGED',
payload: text
};
};
export default emailChanged();
Your connect is miswired
connect(mapStateToProps, null, {emailChanged}) (LoginForm);
It should be something like:
connect(mapStateToProps,
(dispatch) => ({emailChanged: (text) => dispatch(emailChanged(text))})
)(LoginForm);
so that your action actually gets dispatched
and as spotted by emed in comment:
export default emailChanged;
without parentheses.
You defined your callback inside your render() method and not inside the class body. Do it like this:
class LoginForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
}
render() {
return(...);
}
}
Also you shouldn't bind methods inside your render() method. Do it in the constructor of your Component:
class LoginForm extends Component {
constructor(props) {
super(props);
this.onEmailChange.bind(this);
}
onEmailChange(text) {
// do something
}
// other methods
}
Or if you use babel and ES6, you can define your callback with an arrow function, then it will be automatically bound:
class LoginForm extends Component {
onEmailChange = text => {
// do something
};
// other methods
}
See also the react docs about autobinding.
Also your call to connect seems incorrect. If you want to dispatch the action emailChanged it has to look like this:
const mapStateToProps = state => {
return {
email: state.auth.email
};
};
const mapDispatchToProps = dispatch => {
// this put a function emailChanged into your props that will dispatch the correct action
emailChanged: text => dispatch(emailChanged(text))
};
const LoginFormContainer = connect(mapStateToProps, mapDispatchToProps)(LoginForm);
export default LoginFormContainer;
The third argument to connect needs to be a function that knows how to merge the output of mapStateToProps, mapDispatchToProps, and ownProps all into one object that is then used as props for your connected component. I think you're trying to pass that action to the mapDispatchToProps argument, which is the second argument not the third. So, based on what I think you're doing, you probably wanna change your connect line to look like this.
export default connect(mapStateToProps, {emailChanged}) (LoginForm);
Then, export the function from your actions file not the output of calling that function.
export default emailChanged;
Notice I removed the parentheses so it's not being called.
Then make the callback function a method on your class and bind it in the constructor.
constuctor(props) {
super(props);
this.onEmailChange = this.onEmailChange.bind(this);
}
onEmailChange(text) {
this.props.emailChanged(text);
}
Then update onChangeText on that element.
onChangeText={this.onEmailChange}

Categories

Resources