onPressIn getting called twice if view moves - react-native

I created a sort of demo on expo: https://snack.expo.io/#noitsnack/onpressin-twice
I tested this by scanning QR code in expo. This may be a expo bug.
I have wraped the expo symbol (an <Image>) in a <TouchableWithoutFeedback>. Here we see that when we press the expo symbol it causes the score to increase by one, and then the symbol moves to a new location. Sometimes it causes the onPressIn to trigger twice. I don't understand this, why does this happen?
Here is the code to the expo:
import React, { Component } from 'react';
import { Text, View, TouchableWithoutFeedback, Image, Dimensions } from 'react-native';
import IMAGE_EXPO from './assets/expo.symbol.white.png'
const IMAGE_SIZE = 100;
export default class App extends Component {
state = {
score: 0,
...getRandXY()
}
render() {
const { score, x, y } = this.state;
return (
<View style={{flex:1, backgroundColor:'steelblue' }}>
<Text style={{fontSize:72, textAlign:'center'}}>{score}</Text>
<TouchableWithoutFeedback onPressIn={this.addScore}>
<Image source={IMAGE_EXPO} style={{ width:IMAGE_SIZE, height:IMAGE_SIZE, left:x, top:y, position:'absolute' }} />
</TouchableWithoutFeedback>
</View>
)
}
addScore = e => {
this.setState(({ score }) => ({ score:score+1, ...getRandXY() }));
}
}
function getRandXY() {
return {
x: getRandInt(0, Dimensions.get('window').width - IMAGE_SIZE),
y: getRandInt(0, Dimensions.get('window').height - IMAGE_SIZE)
}
}
function getRandInt(min, max)
{
return Math.floor(Math.random()*(max-min+1)+min);
}

Related

React Native how replace image url on error?

I have an app which displays multiple images which I load from the API. Now the problem is some of the images are expired which is causing a problem on Android, in Android the screen starts lagging as soon as the expired image loads on the screen.
I have tried replacing the image source with onError={() => this.imgRefs[img_unique_id].setNativeProps({src: [{uri: this.state.workingUri}]})} this method but its not working.
I cannot use the local state as it is not saving the output in the local state.
I have tried the following code
<Image
source={image.url}
progressiveRenderingEnabled={true}
ref={item.id}
onError={(e) => this.refs[item.id].setNativeProps({source: [{uri: "working image URL"}]})}
resizeMethod={"scale"}>
</Image>
The above code gives me an undefined setNativeProps error, and if I do not use the onError on android it shows me memory leak error.
Here is a complete example of that. To have own state for every FlatList item, I created a class.
import React, { Component, PureComponent } from 'react';
import { FlatList, Text, View, Image } from 'react-native';
class ItemClass extends PureComponent {
state = {
isImageExit: null
}
componentWillMount = () => {
const { task } = this.props;
Image.getSize(task.url, (width, height) => {
if(width>0){
this.setState({ isImageExit: true });
}
else{
this.setState({ isImageExit: false });
}
}, () => {
this.setState({ isImageExit: false });
});
}
render() {
const { isImageExit } = this.state;
const { task } = this.props;
const url = isImageExit ? task.url : 'https://dummyimage.com/600x400/000/fff';
if (isImageExit === null) {
return null;
}
return (
<Image
style={{ height: 200, width: 200 }}
source={{ uri: url }}
/>
);
}
}
export default class App extends Component {
render() {
const data = [
{ url: 'url' },
{ url:'https://cdn.pixabay.com/photo/2017/08/05/18/53/mountain-2585069_1280.jpg' },
];
return (
<View style={{alignItems: 'center', top: 50}}>
<FlatList
data={data}
renderItem={({ item }) => <ItemClass task={item} />}
/>
</View>
);
}
}
I think you should use data received from Api and set state accordingly inside componentWillReceiveProps because I think setting state is the best way to achieve this result.
.
this.setState = { image: { uri: 'image_uri_from_api' } }
Inside <Image> add -
<Image style={ your_custom_style }
source={ this.state.image }
onError={ this.onError.bind(this) }
/>
Inside onError add this -
onError(error){
this.setState({ image: require('your_local_image.path')})
}
Hope it works now !

Accelerometer Problems in React-Native/Expo

I'm building an app on codeHS that utilizes an accelerometer.
I tried dragging and dropping the accelerometer component the website provides into the code I was writing just to see how the component would work.
When I tried to run the code the debugger gave me an error, stating that it couldn't read the addListener event, stating that it was undefined. What is causing the error? I appreciate any insight or tips.
Here is a copy of the code:
import React, { Component } from 'react';
import { AppRegistry, Text, View, Listener, StyleSheet, TouchableOpacity } from 'react-native';
import { Constants, Accelerometer } from 'expo';
export default class App extends Component {
componentWillUnmount() {
this._unsubscribeFromAccelerometer();
}
componentDidMount() {
this._subscribeToAccelerometer();
}
state = {
accelerometerData: { x: 0, y: 0, z: 0 }
};
_subscribeToAccelerometer = () => {
this._acceleroMeterSubscription = Accelerometer.addListener(accelerometerData =>
this.setState({ accelerometerData })
);
};
_unsubscribeFromAccelerometer = () => {
this._acceleroMeterSubscription && this._acceleroMeterSubscription.remove();
this._acceleroMeterSubscription = null;
};
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
<Text>
Accelerometer:
x = {this.state.accelerometerData.x.toFixed(2)}{', '}
y = {this.state.accelerometerData.y.toFixed(2)}{', '}
z = {this.state.accelerometerData.z.toFixed(2)}
</Text>
</Text>
</View>
);
}
}

undefined is not an object(evaluating '_this.refs.toast.show')

My aim is to listen the event for Android homeback button, and then give a toast when continuous click. Here the third component "react-native-easy-toast" is used, fully code snippets shown as below:
import React, { Component } from 'react';
import {View, WebView, Platform, BackHandler, StyleSheet} from 'react-native';
import Toast, {DURATION} from 'react-native-easy-toast';
export default class HomeScreen extends Component {
constructor (props) {
super(props);
}
onNavigationStateChange = (event) => {
this.setState({
navCanGoBack : event.canGoBack,
});
};
componentDidMount() {
if (Platform.OS === 'android') {
BackHandler.addEventListener('hardwareBackPress', this.onHomeBackPress);
}
}
onHomeBackPress = () => {
if (this.state.navCanGoBack) {
this.refs['webview'].goBack();
return true;
} else {
//exit app if exceed 2 seconds
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
return false;
}
this.lastBackPressed = Date.now();
this.refs.toast.show('weill exit app press again');
return true;
}
};
render() {
return (
<View style={{flex:1}}>
<WebView
source={{ uri: 'http://www.test.com/' }}
style={{ width: '100%', height: '100%' }}
ref="webview"
onNavigationStateChange={this.onNavigationStateChange}
/>
<Toast ref="toast" />
</View>
);
}
}
Finally, run the app and error given:
undefined is not an object(evaluating '_this.refs.toast.show')
I am newer to React Native.
Please try
//inside constructor
this.toastRef = React.createRef();
...
<Toast ref={this.toastRef} />
and replace
this.refs.toast.show('weill exit app press again');
to
this.toastRef.current.show('weill exit app press again');

React native not overlapping statusbar

Im learing React native but i dont understand how to get everything to not overlap with the status bar.
I have tried
translucent={true/flase}
hidden
This is expected behaviour, one solution is to add a padding to your top level view equal to current height of status bar or have a StatusBarView that has same height as the status bar.
Refer to this plugin https://github.com/jgkim/react-native-status-bar-size for listening to status bar height changes.
For e.g
import _ from 'lodash';
import React, { Component } from 'react';
import { View, StatusBar} from 'react-native';
import StatusBarSizeIOS from 'react-native-status-bar-size';
const STATUS_BAR_EXTRA_PADDING = 2;
const DEFAULT_STATUS_BAR_HEIGHT = 10;
class StatusBarView extends Component {
state = {
statusBarHeight: _.get(StatusBarSizeIOS, 'currentHeight', DEFAULT_STATUS_BAR_HEIGHT);
}
_handleStatusBarSizeDidChange = (statusBarHeight) => this.setState({ statusBarHeight })
componentDidMount() {
StatusBarSizeIOS.addEventListener('didChange', this._handleStatusBarSizeDidChange);
}
render() {
return (
<View
style={{
height: this.state.statusBarHeight + STATUS_BAR_EXTRA_PADDING,
}}
>
<StatusBar {...this.props} />
</View>
);
}
}
export default StatusBarView;
Now inside your screens you could do something like
import StatusBarView from '{view_path}';
...
render() {
return (
<View>
<StatusBarView barStyle="default" />
<View>{rest of the view}</View>
</View>
);
}

React-native image - unknown named module '../img/2.jpeg'

I'm trying to modify a react-redux tutorial code. I'm trying to load images that are stored locally and the paths are saved in an object.
...
{
"id": 2,
"title": "Redux",
"subtitle": "A State of Mind",
"img": "../img/3.jpeg",
"description": "Redux is a predictable state container for JavaScript apps. It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test."
},
...
I have got the following error (I've restarted the simulator a few times)
Unknown named module: '../img/3.jpeg'
Here's the container (eslint also says - unexpected require())
import React, { Component } from 'react';
import {
Text,
TouchableWithoutFeedback,
View,
LayoutAnimation,
Image
} from 'react-native';
import { connect } from 'react-redux';
import { CardSection } from './common';
import * as actions from '../actions';
class ListItem extends Component {
componentWillUpdate() {
LayoutAnimation.spring();
}
renderDescription() {
const { library, expanded } = this.props;
if (expanded) {
return (
<CardSection>
<Image
source={require(library.img)}
style={styles.imageStyle}
/>
<View>
<Text>{library.subtitle}</Text>
<Text style={{ flex: 1 }}>
{library.description}
</Text>
</View>
</CardSection>
);
}
}
render() {
const { titleStyle } = styles;
const { id, title } = this.props.library;
return (
<TouchableWithoutFeedback
onPress={() => this.props.selectLibrary(id)}
>
<View>
<CardSection>
<Text style={titleStyle}>
{title}
</Text>
</CardSection>
{this.renderDescription()}
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = {
titleStyle: {
fontSize: 18,
paddingLeft: 15
},
descriptionStyle: {
paddingLeft: 10,
paddingRight: 10
},
imageStyle: {
height: 100,
width: 100,
backgroundColor: 'black'
}
};
const mapStateToProps = (state, ownProps) => {
const expanded = state.selectedLibraryId === ownProps.library.id;
return { expanded };
};
export default connect(mapStateToProps, actions)(ListItem);
Apart from the image, everything is working fine - just the image is not there.
You can not dynamically require images in React Native, either do it statically, or use uri like this:
source={{ uri: library.img }}
But take in consideration the you have to put your images under this path
anroid/app/src/main/res/drawable
create drawable folder if it's not created.
and update the json file to remove the ../
Please refer to this issue: https://github.com/facebook/react-native/issues/2481