Function firing when it wants to. How should I fix this? - javascript
I have this video, playing in zindex: -1 with a button and a text input floating over it. The issue is when the text changes, it's supposed to manipulate that state object, not fire the touchable highlight's on click function.
When I use the suggestion given yesterday, the error turns into a warning. If I type 7 random letters in the input box, I'll get 7 warnings saying: "warning bind() you are binding a component method to the component", which means the input box is continuing to call the touchable highlight's function.
I'm using this library for React Native to use it's streaming capabilities: https://github.com/oney/react-native-webrtc. It's pretty nice!
On one of it's examples, https://github.com/oney/RCTWebRTCDemo/blob/master/main.js there are these lines of code I'm fiddling with:
_renderTextRoom() {
return (
<View style={styles.listViewContainer}>
<ListView
dataSource={this.ds.cloneWithRows(this.state.textRoomData)}
enableEmptySections={true}
renderRow={rowData =>
<Text
style={styles.whiteOut}
>{`${rowData.user}: ${rowData.message}`}</Text>}
/>
<TextInput
style={[styles.whiteOut, styles.bgWhite]}
onChangeText={value => this.setState({ textRoomValue: value })}
value={this.state.textRoomValue}
/>
<View style={styles.buttonContainer}>
<TouchableHighlight
style={styles.button}
onPress={this._textRoomPress()}>
<Text style={styles.bgWhite}>Send</Text>
</TouchableHighlight>
</View>
</View>
);
},
When I enter text into the text field, the this._textRoomPress() function nested within the TouchableHighlight is firing. What!? When I comment it out, it doesn't fire.
'use strict';
import React, { Component } from 'react';
import {
Dimensions,
StyleSheet,
Text,
TouchableHighlight,
View,
TextInput,
ListView,
ScrollView
} from 'react-native';
import { userData } from '../utils/Factory';
import io from 'socket.io-client';
var socket_one = 'https://xxxxxxxxxxxxxx.herokuapp.com';
const socket = io.connect(socket_one, { transports: ['websocket'] });
import {
RTCPeerConnection,
RTCMediaStream,
RTCIceCandidate,
RTCSessionDescription,
RTCView,
MediaStreamTrack,
getUserMedia,
} from 'react-native-webrtc';
const configuration = { "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] };
const pcPeers = {};
let localStream;
var width = Dimensions.get('window').width; //full width
var height = Dimensions.get('window').height; //full height
function getLocalStream(isFront, callback) {
MediaStreamTrack.getSources(sourceInfos => {
console.log(sourceInfos);
let videoSourceId;
for (const i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if (sourceInfo.kind == "video" && sourceInfo.facing == (isFront ? "front" : "back")) {
videoSourceId = sourceInfo.id;
}
}
getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 700, // Provide your own width, height and frame rate here
minHeight: 700,
minFrameRate: 30
},
facingMode: (isFront ? "user" : "environment"),
optional: [{ sourceId: sourceInfos.id }]
}
}, function(stream) {
console.log('dddd', stream);
callback(stream);
}, logError);
});
}
function join(roomID) {
socket.emit('join', roomID, function(socketIds) {
console.log('join', socketIds);
for (const i in socketIds) {
const socketId = socketIds[i];
createPC(socketId, true);
}
});
}
function createPC(socketId, isOffer) {
const pc = new RTCPeerConnection(configuration);
pcPeers[socketId] = pc;
pc.onicecandidate = function(event) {
// console.warn('onicecandidate', event.candidate);
if (event.candidate) {
socket.emit('exchange', { 'to': socketId, 'candidate': event.candidate });
}
};
function createOffer() {
pc.createOffer(function(desc) {
console.log('createOffer', desc);
pc.setLocalDescription(desc, function() {
console.log('setLocalDescription', pc.localDescription);
socket.emit('exchange', { 'to': socketId, 'sdp': pc.localDescription });
}, logError);
}, logError);
}
pc.onnegotiationneeded = function() {
console.log('onnegotiationneeded');
if (isOffer) {
createOffer();
}
}
pc.oniceconnectionstatechange = function(event) {
console.log('oniceconnectionstatechange', event.target.iceConnectionState);
if (event.target.iceConnectionState === 'completed') {
setTimeout(() => {
getStats();
}, 1000);
}
if (event.target.iceConnectionState === 'connected') {
createDataChannel();
}
};
pc.onsignalingstatechange = function(event) {
console.log('onsignalingstatechange', event.target.signalingState);
};
pc.onaddstream = function(event) {
console.log('onaddstream', event.stream);
// container.setState({ info: 'One peer join!' });
container.setState({ info: 'Connected!' });
const remoteList = container.state.remoteList;
remoteList[socketId] = event.stream.toURL();
container.setState({ remoteList: remoteList });
};
pc.onremovestream = function(event) {
console.log('onremovestream', event.stream);
};
pc.addStream(localStream);
function createDataChannel() {
if (pc.textDataChannel) {
return;
}
const dataChannel = pc.createDataChannel("text");
dataChannel.onerror = function(error) {
console.log("dataChannel.onerror", error);
};
dataChannel.onmessage = function(event) {
console.log("dataChannel.onmessage:", event.data);
container.receiveTextData({ user: socketId, message: event.data });
};
dataChannel.onopen = function() {
console.log('dataChannel.onopen');
container.setState({ textRoomConnected: true });
};
dataChannel.onclose = function() {
console.log("dataChannel.onclose");
};
pc.textDataChannel = dataChannel;
}
return pc;
}
function exchange(data) {
const fromId = data.from;
let pc;
if (fromId in pcPeers) {
pc = pcPeers[fromId];
} else {
pc = createPC(fromId, false);
}
if (data.sdp) {
console.log('exchange sdp', data);
pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function() {
if (pc.remoteDescription.type == "offer")
pc.createAnswer(function(desc) {
console.log('createAnswer', desc);
pc.setLocalDescription(desc, function() {
console.log('setLocalDescription', pc.localDescription);
socket.emit('exchange', { 'to': fromId, 'sdp': pc.localDescription });
}, logError);
}, logError);
}, logError);
} else {
console.log('exchange candidate', data);
pc.addIceCandidate(new RTCIceCandidate(data.candidate));
}
}
function leave(socketId) {
console.log('leave', socketId);
const pc = pcPeers[socketId];
const viewIndex = pc.viewIndex;
pc.close();
delete pcPeers[socketId];
const remoteList = container.state.remoteList;
delete remoteList[socketId]
container.setState({ remoteList: remoteList });
container.setState({ info: 'One peer leave!' });
}
socket.on('exchange', function(data) {
exchange(data);
});
socket.on('leave', function(socketId) {
leave(socketId);
});
socket.on('connect', function(data) {
console.log('connected');
});
function initStream() {
getLocalStream(true, function(stream) {
localStream = stream;
container.setState({ selfViewSrc: stream.toURL() });
// container.setState({ status: 'ready', info: 'Please enter or create room ID' });
container.setState({ status: 'connect', info: 'Connecting' });
if (userData.inDanger) {
join(0);
} else {
join(userData.userName);
// join(userData.nowPlaying);
}
});
}
function logError(error) {
console.log("logError", error);
}
function mapHash(hash, func) {
const array = [];
for (const key in hash) {
const obj = hash[key];
array.push(func(obj, key));
}
return array;
}
function _textRoomPress() {
if (!container.textRoomValue) {
return
}
const textRoomData = container.textRoomData.slice();
textRoomData.push({ user: 'Me', message: container.textRoomValue });
for (const key in pcPeers) {
const pc = pcPeers[key];
pc.textDataChannel.send(container.textRoomValue);
}
container.setState({ textRoomData, textRoomValue: '' });
}
function getStats() {
const pc = pcPeers[Object.keys(pcPeers)[0]];
if (pc.getRemoteStreams()[0] && pc.getRemoteStreams()[0].getAudioTracks()[0]) {
const track = pc.getRemoteStreams()[0].getAudioTracks()[0];
console.log('track', track);
pc.getStats(track, function(report) {
console.log('getStats report', report);
}, logError);
}
}
let container;
const Stream = React.createClass({
getInitialState: function() {
this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => true });
return {
info: 'Initializing',
status: 'init',
roomID: '',
// isFront: true,
isFront: false,
selfViewSrc: null,
remoteList: {},
textRoomConnected: false,
textRoomData: [],
textRoomValue: '',
};
},
componentDidMount: function() {
container = this;
initStream();
},
_press(event) {
// this.refs.roomID.blur();
this.setState({ status: 'connect', info: 'Connecting' });
join(userData.userName);
// join(this.state.roomID);
},
_switchVideoType() {
const isFront = !this.state.isFront;
this.setState({ isFront });
getLocalStream(isFront, function(stream) {
if (localStream) {
for (const id in pcPeers) {
const pc = pcPeers[id];
pc && pc.removeStream(localStream);
}
localStream.release();
}
localStream = stream;
container.setState({ selfViewSrc: stream.toURL() });
for (const id in pcPeers) {
const pc = pcPeers[id];
pc && pc.addStream(localStream);
}
});
},
receiveTextData(data) {
const textRoomData = this.state.textRoomData.slice();
textRoomData.push(data);
this.setState({ textRoomData, textRoomValue: '' });
},
_textRoomPress() {
if (!this.state.textRoomValue) {
return
}
const textRoomData = this.state.textRoomData.slice();
textRoomData.push({ user: 'Me', message: this.state.textRoomValue });
for (const key in pcPeers) {
const pc = pcPeers[key];
pc.textDataChannel.send(this.state.textRoomValue);
}
this.setState({ textRoomData, textRoomValue: '' });
},
_renderTextRoom() {
return (
<View style={styles.listViewContainer}>
<ListView
dataSource={this.ds.cloneWithRows(this.state.textRoomData)}
enableEmptySections={true}
renderRow={rowData =>
<Text
style={styles.whiteOut}
>{`${rowData.user}: ${rowData.message}`}</Text>}
/>
<TextInput
style={[styles.whiteOut, styles.bgWhite]}
onChangeText={value => this.setState({ textRoomValue: value })}
value={this.state.textRoomValue}
/>
<View style={styles.buttonContainer}>
<TouchableHighlight
style={styles.button}
onPress={this._textRoomPress()}>
<Text style={styles.bgWhite}>Send</Text>
</TouchableHighlight>
</View>
</View>
);
},
render() {
return (
<View style={styles.container}>
{
mapHash(this.state.remoteList, (remote, index) => {
return (
<ScrollView key={index}>
<RTCView key={index} streamURL={this.state.selfViewSrc} style={styles.remoteView}>
<View style={styles.buttonContainer}>
<TouchableHighlight
style={styles.button}
onPress={this._switchVideoType}>
<Text>Switch camera</Text>
</TouchableHighlight>
</View>
<View style={styles.bottomContainer}>
{this.state.textRoomConnected && this._renderTextRoom()}
</View>
</RTCView>
)
})
}
</View>
);
}
});
const styles = StyleSheet.create({
container: {
flex: 10,
// justifyContent: 'center',
backgroundColor: 'rgba(0,0,0, .0)',
},
topContainer: {
flex: 10,
backgroundColor: '#c7c7c7',
},
bottomContainer: {
flex: 1,
justifyContent: 'flex-end',
// backgroundColor: '#ffeeff',
'zIndex': 1,
backgroundColor: 'rgba(0,0,0, .0)',
},
selfView: {
width: 0,
height: 0
},
remoteView: {
flex: 1,
'zIndex': -1,
// backgroundColor: '#c7c7c7',
backgroundColor: '#f0f0f0',
width: width,
height: height - 25,
resizeMode: 'stretch', // or 'stretch'
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
listViewContainer: {
height: 150,
},
buttonContainer: {
height: 50,
// backgroundColor: 'powderblue',
justifyContent: 'center',
alignItems: 'center',
},
button: {
marginTop: 50,
marginBottom: 50,
padding: 10,
paddingLeft: 30,
paddingRight: 30,
borderWidth: 1,
borderColor: 'rgba(0, 0, 0, .75)',
},
whiteOut: {
// color: "#ffffff",
color: "#000",
},
bgWhite: {
// backgroundColor: "#ffffff"
},
listView: {
// backgroundColor: "#ffffff",
flex: 10,
// flexDirection: 'row',
// justifyContent: 'center',
// alignItems: 'center',
}
});
export default Stream;
Replace it with this._textRoomPress.bind(this)
It is not firing arbitrarily, it is firing every time a render is issued. That happens because whatever you pass to an object as a prop is evaluated before being passed (as the parameters of a function are, basically), and so what you are passing is the returning value of the function, which of course is not what you want. By passing this._textRoomPress (with the optional bind in case that you want to keep the context of the object), you pass a reference to the function that will be later called by the component on the appropriate time (when the element is pressed).
Since you're using createClass and not the es6 syntax, all the methods are already autobinding to the Component. Simply just change your onPress to:
onPress={this._textRoomPress}>
If you use onPress={this._textRoomPress()}> It is instantly invoking that function anytime your component gets rendered.
In javascript you use <function name>() to invoke a function... What you are doing here is simply invoking that function every time that _renderTextRoom()gets called rather than assigning it to the onPress prop. What I would suggest is that you pass an anonymous function in as the prop (without calling it) which than returns the invocation of this._textRoomPress. ES6 arrow functions make this super easy because they do not bind their own this more info here
<View style={styles.buttonContainer}>
<TouchableHighlight
style={styles.button}
onPress={() => this._textRoomPress()}>
<Text style={styles.bgWhite}>Send</Text>
</TouchableHighlight>
</View>
Related
Expo Notification for ios and android err "Trigger of type: calendar is not supported on Android"
Below is a code example. The whole problem is that I can not set up a notification for a specific day and month. I get the error " Error: Failed to schedule the notification. Trigger of type: calendar is not supported on Android." Everything works until I specify a date. Any time specified in hours and minutes works but does not work on the specified dates. import { StatusBar } from 'expo-status-bar'; import React, {useEffect, useState, useRef} from 'react'; import { StyleSheet, Text, View, TouchableOpacity } from 'react-native'; import * as Notifications from 'expo-notifications'; import Constants from 'expo-constants'; import storage from "#react-native-async-storage/async-storage"; //1. import the library //2. get permission //3. do push notifications on button click //4. schedule push notifications Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: true }) }); export default function App() { const [notification, setNotification] = useState(false); const notificationListener = useRef(); const responseListener = useRef(); useEffect(() => { const getPermission = async () => { if (Constants.isDevice) { const { status: existingStatus } = await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; if (existingStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== 'granted') { alert('Enable push notifications to use the app!'); await storage.setItem('expopushtoken', ""); return; } const token = (await Notifications.getExpoPushTokenAsync()).data; await storage.setItem('expopushtoken', token); } else { alert('Must use physical device for Push Notifications'); } if (Platform.OS === 'android') { Notifications.setNotificationChannelAsync('default', { name: 'default', importance: Notifications.AndroidImportance.MAX, vibrationPattern: [0, 250, 250, 250], lightColor: '#FF231F7C', }); } } getPermission(); notificationListener.current = Notifications.addNotificationReceivedListener(notification => { setNotification(notification); }); responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {}); return () => { Notifications.removeNotificationSubscription(notificationListener.current); Notifications.removeNotificationSubscription(responseListener.current); }; }, []); const onClick = async () => { await Notifications.scheduleNotificationAsync({ content: { title: "Title 1", body: "body 1", data: { data: "data goes here" } }, trigger: { day: 6, month: 0, hour: 17, minute: 30, } }); await Notifications.scheduleNotificationAsync({ content: { title: "Title 2", body: "body 2", data: { data: "data goes here" } }, trigger: { day: 8, month: 0, hour: 15, minute: 25, } }); } return ( <View style={styles.container}> <TouchableOpacity onPress={onClick}> <Text style={{backgroundColor: 'red', padding: 10, color: 'white'}}>Click me to send a push notification</Text> </TouchableOpacity> <StatusBar style="auto" /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }, });
How to create a compass that points to specific coordinates (React-Native)
Here is what I have for now: import { Alert, Animated, Easing, Linking, StyleSheet, Text, View, } from "react-native"; import React, { useEffect, useState } from "react"; import * as Location from "expo-location"; import * as geolib from "geolib"; import { COLORS } from "../../assets/Colors/Colors"; export default function DateFinder() { const [hasForegroundPermissions, setHasForegroundPermissions] = useState(null); const [userLocation, setUserLocation] = useState(null); const [userHeading, setUserHeading] = useState(null); const [angle, setAngle] = useState(0); useEffect(() => { const AccessLocation = async () => { function appSettings() { console.warn("Open settigs pressed"); if (Platform.OS === "ios") { Linking.openURL("app-settings:"); } else RNAndroidOpenSettings.appDetailsSettings(); } const appSettingsALert = () => { Alert.alert( "Allow Wassupp to Use your Location", "Open your app settings to allow Wassupp to access your current position. Without it, you won't be able to use the love compass", [ { text: "Cancel", onPress: () => console.warn("Cancel pressed"), }, { text: "Open settings", onPress: appSettings }, ] ); }; const foregroundPermissions = await Location.requestForegroundPermissionsAsync(); if ( foregroundPermissions.canAskAgain == false || foregroundPermissions.status == "denied" ) { appSettingsALert(); } setHasForegroundPermissions(foregroundPermissions.status === "granted"); if (foregroundPermissions.status == "granted") { const location = await Location.watchPositionAsync( { accuracy: Location.Accuracy.BestForNavigation, activityType: Location.ActivityType.Fitness, distanceInterval: 0, }, (location) => { setUserLocation(location); } ); const heading = await Location.watchHeadingAsync((heading) => { setUserHeading(heading.trueHeading); }); } }; AccessLocation().catch(console.error); }, []); useEffect(() => { if (userLocation != null) { setAngle(getBearing() - userHeading); rotateImage(angle); } }, [userLocation]); const textPosition = JSON.stringify(userLocation); const getBearing = () => { const bearing = geolib.getGreatCircleBearing( { latitude: userLocation.coords.latitude, longitude: userLocation.coords.longitude, }, { latitude: 45.47200370608976, longitude: -73.86246549592089, } ); return bearing; }; const rotation = new Animated.Value(0); console.warn(angle); const rotateImage = (angle) => { Animated.timing(rotation, { toValue: angle, duration: 1000, easing: Easing.bounce, useNativeDriver: true, }).start(); }; //console.warn(rotation); return ( <View style={styles.background}> <Text>{textPosition}</Text> <Animated.Image source={require("../../assets/Compass/Arrow_up.png")} style={[styles.image, { transform: [{ rotate: `${angle}deg` }] }]} /> </View> ); } const styles = StyleSheet.create({ background: { backgroundColor: COLORS.background_Pale, flex: 1, // justifyContent: "flex-start", //alignItems: "center", }, image: { flex: 1, // height: null, // width: null, //alignItems: "center", }, scrollView: { backgroundColor: COLORS.background_Pale, }, }); I think that the math I'm doing must be wrong because the arrow is pointing random directions spinning like crazy and not going to the coordinate I gave it. Also, I can't seem to use the rotateImage function in a way that rotation would be animated and i'd be able to use it to animate the image/compass. If anyone could help me out i'd really appreciate it I've been stuck on this for literally weeks.
TypeError: undefined is not a function (near '...this.state.profile.map...')
I am getting error again and again . I don't know why I am getting this error. The response I am getting is also an array, I tried with console.log . Below is the proof Response From the axios Api: { "CCompany": "Testing Company", "CFName": "Rehan", "CLName": "ahmed", "CMName": "", "CTelHome": "1-232-2323232", "UID": "700002" } Below is the code: import React, { Component } from 'react'; import { View, Text, Dimensions, BackHandler, ToastAndroid } from 'react-native'; import axios from 'axios'; import Card from './Card'; import CardSection from './CardSection'; import ProfileDetails from './ProfileDetails'; import AsyncStorage from '#react-native-community/async-storage'; // Create a component class ProfileActivity extends Component { constructor() { super(); this.state = { profile: [], setUID: '', isloading: true, }; } state = { canBeClosed: false } componentWillUnmount() { BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton); } handleBackButton = () => { if (this.props.navigation.isFocused()) { if (this.state.canBeClosed) return this.state.canBeClosed = false; else { setTimeout(() => { this.state.canBeClosed = false }, 3000); ToastAndroid.show("Press Again To Exit !", ToastAndroid.LONG); return this.state.canBeClosed = true } } }; async componentDidMount() { try { if (this.state.setUID == null && this.state.profile == null) { console.log('profile remove'); const user = await AsyncStorage.getItem('responseJson'); const parsed = JSON.parse(user); if (parsed !== null) { this.setState({ setUID: parsed.UID }); } axios.get('https:/new.didx.net/didxapi/UserInfo.php?UID=' + this.state.setUID) .then(response => this.setState({ profile: response.data })); } else { this.setState({ setUID: "" }); this.setState({ profile: "" }); console.log('not remove'); const user = await AsyncStorage.getItem('responseJson'); const parsed = JSON.parse(user); if (parsed !== null) { this.setState({ setUID: parsed.UID }); } axios.get('https:/new.didx.net/didxapi/UserInfo.php?UID=' + this.state.setUID) .then(response => this.setState({ profile: response.data })); } } catch (error) { alert('Server Error!') } BackHandler.addEventListener('hardwareBackPress', this.handleBackButton); } renderProfile() { if (this.state.profile) { console.log(this.state.profile); return this.state.profile.map(profile => ( <ProfileDetails key={profile.UID} profile={profile} /> )); } } render() { return ( <View style={styles.container}> {this.renderProfile()} </View> ); } } export default ProfileActivity; const h = Dimensions.get('screen').height * 0.01; const w = Dimensions.get('screen').width * 0.01; const styles = { container: { flex: 1, backgroundColor: '#fff' }, ViewStyle: { paddingTop: h * 5, }, TextStyle: { justifyContent: 'flex-start', // alignSelf: 'center', color: '#000', fontWeight: 'bold', fontSize: 20, padding: 5, fontFamily: 'Roboto', maxWidth: w * 50, } } I tried everything I could to solve this problem.
.map expects an array ... but your axios.get('https:/new.didx.net/didxapi/UserInfo.php?UID=' + this.state.setUID) call returns an object like { UID: "1", CFName: "1", CMName: "", CLName: "1", CCompany: "1", CTelHome: "2" }
How to display each element of an array with different timeout delay and each delay time being a value inside each element in react
Every element of the array should be displayed for some time and the time for which each element is displayed should be determined by a value in each element. let array=[{display:"a",time:10},{display:"b",time:15},{display:"c",time:22}] class App extends React.Component{ state={stateDisplay:"", stateTime:"" } componentWillMount(){ var i=0; let handle=setInterval(()=>{ var element= array[i] this.setState({ stateDisplay:element.display, stateTime:element.time, }) i=i+1; if(i===array.length){ clearInterval(handle) } },10000) } render(){ return( <div> {this.state.stateDisplay} </div> )}} i have done something like this but using setinterval the delay can only be set for a constant time,here 10s. I want the first element to display for 10s and then the next element for 15s, third for 22s which is the time value for each element of the array. I know i cant do that using setinterval is there a way to do this using Settimeout?
This was almost like a little challenge, heres what i managed to come up with, its in typescript, if you need js, just remove interfaces and type annotations /* eslint-disable #typescript-eslint/no-explicit-any */ /* eslint-disable prettier/prettier */ /* eslint-disable no-shadow */ /* eslint-disable no-console */ import React, { FC, useState, useEffect, useCallback } from 'react'; import { View, Button, Text } from 'react-native'; interface Data { duration: number; bgColor: string; } const dataArr: Data[] = [ { duration: 3, bgColor: 'tomato' }, { duration: 6, bgColor: 'skyblue' }, { duration: 9, bgColor: 'gray' }, ]; const Parent = () => { const [currentIdx, setCurrentIdx] = useState<number>(0); const [elementData, setElementData] = useState<Data>(dataArr[currentIdx]); useEffect(() => { console.log('idx', currentIdx); if (currentIdx > dataArr.length) return; setElementData({ ...dataArr[currentIdx] }); }, [currentIdx]); const pushNext = () => { setCurrentIdx(currentIdx + 1); }; const handleRestart = () => { setCurrentIdx(0); setElementData({ ...dataArr[0] }); }; return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Timer data={elementData} onCountDownComplete={pushNext} restart={handleRestart} /> </View> ); }; interface Props { data: Data; onCountDownComplete: () => void; restart: () => void; } const Timer: FC<Props> = ({ data, onCountDownComplete, restart }) => { const [seconds, setSeconds] = useState<number>(data.duration); // update on data change useEffect(() => { setSeconds(data.duration); }, [data]); const callback = useCallback(() => { onCountDownComplete(); }, [onCountDownComplete]); useEffect(() => { let interval: any = null; if (seconds > -1) { interval = setInterval(() => { if (seconds - 1 === -1) { callback(); } else { setSeconds(seconds - 1); } }, 1000); } else { return; } return () => { clearInterval(interval); }; }, [seconds, callback]); return ( <View style={{ backgroundColor: data.bgColor, padding: 16, borderRadius: 10 }} > <Text style={{ marginBottom: 24 }}>{seconds}</Text> <Button title="restart" onPress={restart} /> </View> ); };
how to use ES6 with react native
Working with React-Native and trying to learn ES6 syntax. I had a similar issue yesterday and got the solution. I added .bind(this) to my my function calls and the problem was solved. I ran into the same issue again with another function call and I cannot track down what is going on. The error message is the same. undefined is not a object (evaluating 'this.props.drawer.open') The function is: onClickMenu () { this.props.drawer.open(); } and it is being called with this: onPress={this.onClickMenu.bind(this)} Here is the entire code. If you see something other than this issue that doesn't look right let me know please! *note I have replaced "var" with "let". From what I've read it is proper ES6 syntax to do that everywhere? 'use strict'; const React = require('react-native'); const { Text, View, Component, StyleSheet, SwitchAndroid } = React; import { Button } from 'react-native-material-design'; import Store from 'react-native-simple-store'; import Underscore from 'underscore'; import RNGMap from 'react-native-gmaps'; import Polyline from 'react-native-gmaps/Polyline'; import Icon from 'react-native-vector-icons/Ionicons'; import SettingsService from './../settings/settings.service'; //import subdivisions from './subdivisions.json'; import commonStyles from './../common/styles'; let accessToken = null; let userId = null; let routeId = null; let subdivisionId = null; SettingsService.init('Android'); class Map extends Component { constructor(props) { super(props) this.state = { odometer: 0, mapWidth: 300, mapHeight: 300, enabled: false, isMoving: false, currentLocation: undefined, locationManager: undefined, paceButtonIcon: 'Start Trip', navigateButtonIcon: 'navigate', paceButtonStyle: commonStyles.disabledButton, // mapbox center: { lat: 40.7223, lng: -73.9878 }, zoom: 10, markers: [] } } componentDidMount() { Store.get('token').then((token) => { accessToken = token.access_token; userId = token.userId; }); let me = this, gmap = this.refs.gmap; this.locationManager = this.props.locationManager; // location event this.locationManager.on("location", function(location) { console.log('- location: ', JSON.stringify(location)); me.setCenter(location); gmap.addMarker(me._createMarker(location)); me.setState({ odometer: (location.odometer / 1000).toFixed(1) }); // Add a point to our tracking polyline if (me.polyline) { me.polyline.addPoint(location.coords.latitude, location.coords.longitude); } }); // http event this.locationManager.on("http", function(response) {}); // geofence event this.locationManager.on("geofence", function(geofence) {}); // error event this.locationManager.on("error", function(error) { console.log('- ERROR: ', JSON.stringify(error)); }); // motionchange event this.locationManager.on("motionchange", function(event) { me.updatePaceButtonStyle(); }); // getGeofences this.locationManager.getGeofences(function(rs) { }, function(error) { console.log("- getGeofences ERROR", error); }); SettingsService.getValues(function(values) { values.license = "eddbe81bbd86fa030ea466198e778ac78229454c31100295dae4bfc5c4d0f7e2"; values.orderId = 1; values.stopTimeout = 0; //values.url = 'http://192.168.11.120:8080/locations'; me.locationManager.configure(values, function(state) { me.setState({ enabled: state.enabled }); if (state.enabled) { me.initializePolyline(); me.updatePaceButtonStyle() } }); }); this.setState({ enabled: false, isMoving: false }); } _createMarker(location) { return { title: location.timestamp, id: location.uuid, icon: require("image!transparent_circle"), anchor: [0.5, 0.5], coordinates: { lat: location.coords.latitude, lng: location.coords.longitude } }; } initializePolyline() { // Create our tracking Polyline let me = this; Polyline.create({ width: 12, points: [], geodesic: true, color: '#2677FF' }, function(polyline) { me.polyline = polyline; }); } onClickMenu () { this.props.drawer.open(); } onClickEnable() { let me = this; if (!this.state.enabled) { this.locationManager.start(function() { me.initializePolyline(); }); } else { this.locationManager.resetOdometer(); this.locationManager.stop(); this.setState({ markers: [{}], odometer: 0 }); this.setState({ markers: [] }); if (this.polyline) { this.polyline.remove(function(result) { me.polyline = undefined; }); } } this.setState({ enabled: !this.state.enabled }); this.updatePaceButtonStyle(); } onClickPace() { if (!this.state.enabled) { return; } let isMoving = !this.state.isMoving; this.locationManager.changePace(isMoving); this.setState({ isMoving: isMoving }); this.updatePaceButtonStyle(); } onClickLocate() { let me = this; this.locationManager.getCurrentPosition({ timeout: 30 }, function(location) { me.setCenter(location); }, function(error) { console.error('ERROR: getCurrentPosition', error); me.setState({ navigateButtonIcon: 'navigate' }); }); } onRegionChange() {} setCenter(location) { this.setState({ navigateButtonIcon: 'navigate', center: { lat: location.coords.latitude, lng: location.coords.longitude }, zoom: 16 }); } onLayout() { let me = this, gmap = this.refs.gmap; this.refs.workspace.measure(function(ox, oy, width, height, px, py) { me.setState({ mapHeight: height, mapWidth: width }); }); } updatePaceButtonStyle() { let style = commonStyles.disabledButton; if (this.state.enabled) { style = (this.state.isMoving) ? commonStyles.redButton : commonStyles.greenButton; } this.setState({ paceButtonStyle: style, paceButtonIcon: (this.state.enabled && this.state.isMoving) ? 'Stop Trip' : 'Start Trip' }); } render() { return ( <View style={commonStyles.container}> <View style={commonStyles.topToolbar}> <Icon.Button name="android-options" onPress={this.onClickMenu.bind(this)} backgroundColor="transparent" size={30} color="#000" style={styles.btnMenu} underlayColor={"transparent"} /> <Text style={commonStyles.toolbarTitle}>Background Geolocation</Text> <SwitchAndroid onValueChange={this.onClickEnable.bind(this)} value={this.state.enabled} /> </View> <View ref="workspace" style={styles.workspace} onLayout={this.onLayout.bind(this)}> <RNGMap ref={'gmap'} style={{width: this.state.mapWidth, height: this.state.mapHeight}} markers={this.state.markers} zoomLevel={this.state.zoom} onMapChange={(e) => console.log(e)} onMapError={(e) => console.log('Map error --> ', e)} center={this.state.center} /> </View> <View style={commonStyles.bottomToolbar}> <Icon.Button name={this.state.navigateButtonIcon} onPress={this.onClickLocate.bind(this)} size={25} color="#000" underlayColor="#ccc" backgroundColor="transparent" style={styles.btnNavigate} /> <Text style={{fontWeight: 'bold', fontSize: 18, flex: 1, textAlign: 'center'}}>{this.state.odometer} km</Text> <Button raised={true} text={this.state.paceButtonIcon} onPress={this.onClickPace.bind(this)} overrides={{backgroundColor:"#e12429",textColor:"#ffffff"}} style={this.state.paceButtonStyle}></Button> <Text> </Text> </View> </View> ); } }; const styles = StyleSheet.create({ workspace: { flex: 1 } }); module.exports = Map; UPDATE: debugging via adb in the terminal shows the same error So here is rest of code. to troubleshoot. I added the project files to a plunker. it is a demo project that i am working with. plunker 'use strict'; const React = require('react-native'); const { Text, Component, StyleSheet, AppRegistry } = React; import Map from './map/map'; import Drawer from 'react-native-drawer'; import Settings from './settings/settings.android'; import Icon from 'react-native-vector-icons/Ionicons'; import BackgroundGeolocation from 'react-native-background-geolocation-android'; global.bgGeo = BackgroundGeolocation; class App extends Component { onClickMenu() { this.props.refs.drawer.open(); } render() { return ( <Drawer ref="drawer" side="right" acceptPan={false} content={<Settings drawer={this.refs.drawer} locationManager={BackgroundGeolocation} />}> <Map drawer={this.refs.drawer} locationManager={BackgroundGeolocation} /> </Drawer> ); } }; module.exports = App; UPDATE:
I dont think you can pass through refs to components in such a way, certainly it would not work in React and I dont think it would work in such a way in React-Native either. I'm not clear why you are trying to .open the Drawer from the Map component as it looks like the Map component can only be accessed when the Drawer is open, but, if you want to access parent behaviours from children a good pattern is to pass through functions for children to execute (you could argue that this is actually bad and that passing events around is a more robust pattern). I've never used the library so I'm not totally clear on its usage but you can pass functions through like this: class Application extends Component { closeControlPanel = () => { this.refs.drawer.close() }; openControlPanel = () => { this.refs.drawer.open() }; render () { return ( <Drawer ref="drawer" content={<ControlPanel />} > <Map onMenuClose={ this.closeControlPanel.bind( this ) } /> </Drawer> ) } }) In this case this.props.onMenuClose should be attached to an action, which, when executed will trigger the function from the parent and execute the this.refs.drawer.close function.