How to check if a component is mounted in React-Native ES6 - react-native

I am setting a listener in my application and using force update whenever it is broadcasted but it gives error forceUpdate cant be called on unmounted component. How can I check if a component is mounted now that the isMounted() function is deprecated.
'use strict';
var React = require('react-native');
import ExpAndroid from './ExpAndroid';
var {
AppRegistry,
Image,
ListView,
TouchableHighlight,
StyleSheet,
Text,
View,
Component,
AsyncStorage,
Navigator,
DeviceEventEmitter
} = React;
var rowID;
var img=require('./resource/ic_pause_white.png');
class Example1 extends Component{
constructor(props) {
super(props);
this.state = {
};
}
componentWillMount(){
rowID = this.props.rowIdentity;
console.log("rowID "+rowID);
}
componentDidMount(){
console.log('component mounted')
this.start();
DeviceEventEmitter.addListener('playMusicStatus', (data)=> {
if(data.playMusic==true){
img=require('./resource/ic_pause_white.png');
rowID++;
this.setState(this.state);
ExpAndroid.someMethod1("someurl);
}
});
}
componentWillUnmount(){
console.log('componentwill unmounted')
}
start() {
var url = "some url";
ToastAndroid.prepareToPlay(url,true);
}
render() {
return (
<Image source={require('./resource/album_back.png')} style={styles.background}>
<Image
source={{uri:this.state.trackDetails[rowID].thumnail_loc}}
style={styles.thumbnail}
/>
<View style={styles.flowRow}>
<Text
style={styles.titles}
>text1 + {rowID}: </Text>
<Text
style={styles.titles}
>{this.state.details[rowID].text1}</Text>
</View>
<View style={styles.flowRow}>
<Text
style={styles.titles}
>text2 : </Text>
<Text
style={styles.titles}
>{this.state.details[rowID].text2}</Text>
</View>
<View style={styles.flowRow}>
<Text
style={styles.titles}
>Text3 : </Text>
<Text
style={styles.titles}
>{this.state.Details[rowID].Text3}</Text>
</View>
<View style={styles.flowRow}>
<Text
style={styles.titles}
>Text4 : </Text>
<Text
style={styles.titles}
>{this.state.details[rowID].Text4}</Text>
</View>
</Image>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
},
background: {
flex: 1,
width: null,
height: null,
},
flowRow : {
flexDirection :'row',
},
flowRowPlay : {
flexDirection :'row',
alignSelf:'center',
},
backgroundImage: {
flex: 1,
resizeMode: 'cover', // or 'stretch'
},
thumbnail: {
width: 100,
height: 120,
alignSelf:'center',
margin :30
},
controls: {
width: 30,
height: 30,
margin:20
},
titles: {
fontSize: 15,
margin:20,
color: 'white',
},
timings: {
fontSize: 12,
margin:5,
color: 'white',
},
});
module.exports = Example1;

You can handle this yourself in your component:
componentDidMount() {
this._mounted = true;
}
componentWillUnmount() {
this._mounted = false;
}
Then you can check the value of this._mounted in your listener.
Please note that using forceUpdate() should be avoided https://facebook.github.io/react/docs/component-api.html#forceupdate
Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render(). This makes your component "pure" and your application much simpler and more efficient.

What I did was changing the callback in componentWillMount.
let asyncCallback;
componentDidMount(){
asyncCallback = res=> this.setState({data: res});
asyncTask(asyncCallback);
}
componentWillUnmount(){
asyncCallback = ()=> console.log("AsyncCallback called but component has unmounted");
}

Using ReactUpdateQueue, you can avoid managing your own isMounted state.
const ReactUpdateQueue = require('ReactUpdateQueue');
// Pass the ref to your component.
if (ReactUpdateQueue.isMounted(view)) {
// Your component is mounted!
}

Related

Results do not update after a change of state

I have a problem, when I do a search, I get the data from my API, the first time I do a search, everything is fine, all the data is displayed. However, when I do a second search immediately, nothing is updated.
I put in console.log, and I see that I'm getting this data back, yet the display is not updated.
import React, { Component } from "react";
import { SafeAreaView, StyleSheet } from "react-native";
import Search from "./Component/Search";
export default class App extends Component {
render() {
return (
<SafeAreaView style={styles.container}>
<Search />
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
import React from "react";
import { View, TextInput, Button, FlatList, StyleSheet } from "react-native";
import LivreItem from "../Component/LivreItem";
class Search extends React.Component {
constructor(props) {
super(props);
this.inputValue = "";
this.state = { livres: [] };
}
searchBooks = async () => {
const key = "&key=XXXXXXXXXXXXXXXXXXXXXXX";
const url = "https://www.googleapis.com/books/v1/volumes?q=" + this.inputValue + key;
return fetch(url)
.then(response => response.json())
.catch(e => {
console.log("Une erreur s'est produite");
console.log(e);
});
};
getBooks = () => {
if (this.inputValue.length > 0) {
this.searchBooks()
.then(data => this.setState({ livres: data.items }))
.catch(reject => console.log(reject));
}
};
changeText = text => {
this.inputValue = text;
};
render() {
return (
<View style={styles.header_container}>
<View style={styles.sub_container}>
<TextInput
onChangeText={text => this.changeText(text)}
style={styles.input}
placeholder="Ex: Harry Potter"
/>
<Button
style={styles.button}
title="Rechercher"
onPress={() => this.getBooks()}
/>
</View>
<FlatList
style={styles.list}
data={this.state.livres}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <LivreItem livre={item.volumeInfo} />}
/>
</View>
);
}
}
const styles = StyleSheet.create({
sub_container: {
justifyContent: "space-between",
flexDirection: "row",
marginTop: 30,
paddingRight: 10,
paddingLeft: 10
},
header_container: {
flex: 1,
flexDirection: "column",
padding: 10
},
input: {
borderRadius: 4,
borderWidth: 0.5,
borderColor: "#d6d7da",
width: 150,
paddingLeft: 5
},
button: {
borderRadius: 4
},
list: {
paddingLeft: 15,
paddingRight: 15
}
});
export default Search;
import React from "react";
import { View, StyleSheet, Image, Text } from "react-native";
class LivreItem extends React.Component {
constructor(props) {
super(props);
this.state = { livre: this.props.livre};
this.description =
this.state.livre.description === null || this.state.livre.description === undefined
? "Pas de description disponible"
: this.state.livre.description;
this.img = this.state.livre.imageLinks;
this.image =
this.img === undefined ||
this.img.smallThumbnail === undefined ||
this.img.smallThumbnail === null
? null
: this.state.livre.imageLinks.smallThumbnail;
}
render() {
return (
<View style={styles.content}>
<View>
<Image style={styles.image} source={{ uri: this.image }} />
<Image style={styles.image} source={this.image} />
</View>
<View style={styles.content_container}>
<Text style={styles.titre}>{this.state.livre.title}</Text>
<Text style={styles.description} numberOfLines={4}>
{this.description}
</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
content: {
height: 125,
flexDirection: "row",
marginTop: 15
},
content_container: {
flexDirection: "column",
flexShrink: 1,
marginLeft: 10
},
image: {
width: 100,
height: 100
},
titre: {
fontWeight: "bold",
flexWrap: "wrap"
},
description: {
flexWrap: "wrap"
}
});
export default LivreItem;
Thanks.
Configure the prop extraData in Flatlist component ala:
<FlatList
extraData={this.state.livres}
/>
Pass a boolean value to the FlatList extraData.
<FlatList
extraData={this.state.refresh}
/>

Using dynamic styles in react native

I've a couple of buttons in my react native app, on pressing any button, color should change. Code is as given below
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
Button,
TouchableHighlight,
Image,
Alert
} from 'react-native';
export default class Home extends Component {
constructor(props) {
super(props);
this.state= {
clicks: 0,
show: true,
isbuttonpress: false
};
}
onButtonState = () => {
this.setState({isbuttonpress: true});
}
render() {
return (
<View style={ [styles.container] }>
<Text style= { [styles.header] }>How likely is it that you would recommend this company to a friend or colleague?</Text>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>0</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>1</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>2</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>3</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>4</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>5</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.nextContainer, styles.goback] } onPress = { () => this.onNextButtonState() }>
<Text style = { styles.gobacktext }>Next</Text>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
header: {
fontSize: 20,
alignItems: 'center',
padding: 20,
},
container: {
alignItems: 'center',
justifyContent: 'center'
},
buttonContainer: {
height:45,
justifyContent: 'center',
alignItems: 'center',
marginBottom:20,
width:45,
}, gobackred: {
backgroundColor: "#00a2b2",
},
goback: {
backgroundColor: "#00b5ec",
},
gobacktext: {
color: 'white',
},
nextContainer: {
height:45,
marginTop: 15,
justifyContent: 'center',
alignItems: 'center',
marginBottom:20,
width:250,
borderRadius:30,
}
})
Layout is as given below:
Whenever any option is selected, color of that button should change. But in my code, color of all buttons changes when clicked on any button. How can I fix it?
This happens because you used one state this.state.isbuttonpress for all button try to make the component of this code.
your TouchableHighlight Component
DemoButton.js
import React, { Component } from "react";
import { View, Text, TouchableHighlight } from "react-native";
export default class DemoButton extends Component {
constructor(props) {
super(props);
this.state = { isbuttonpress: false };
}
onButtonState = () => {
this.setState({ isbuttonpress: !this.state.isbuttonpress });
};
render() {
return (
<View>
<TouchableHighlight
style={{ backgroundColor: this.state.isbuttonpress ? "red" : "blue" }}
onPress={this.onButtonState}
>
<Text>{this.props.buttonTitle}</Text>
</TouchableHighlight>
</View>
);
}
}
App.js
import DemoButton from "./DemoButton";
export default class Heal extends Component {
constructor(props) {
super(props);
this.state = { isbuttonpress: false };
}
render() {
return (
<View>
<Header />
<DemoButton buttonTitle="0"/>
<DemoButton buttonTitle="1"/>
<DemoButton buttonTitle="2"/>
<DemoButton buttonTitle="3"/>
<DemoButton buttonTitle="4"/>
<DemoButton buttonTitle="5"/>
</View>
)
}
}

Call functions inside WebView with injectJavaScript

I've been looking for examples of injectJavaScript. On GitHub I found a few which, I guess for testing, do:
injectJavaScript={()=>'alert("Injected JS ")'}
But I can't make it work. I thought that perhaps I had to wait for the WebView to be loaded, but still no luck.
Here my test:
export default class App extends React.Component {
constructor( props ){
super( props );
this.state = {
loaded: false
};
}
webviewDidLoad(){
this.setState({loaded: true});
}
render() {
return (
<WebView
source={ webview }
injectJavaScript={ this.state.loaded ? ()=>'alert("Injected JS")' : null }
onLoadEnd={ this.webviewDidLoad.bind(this) }
/>
);
}
}
Is the only way to communicate to the WebView through strings and props? No way to communicate with WebView methods passing native javascript objects?
Thanks for your help!
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
TouchableHighlight,
WebView,
} from 'react-native';
let jsCode = `
document.querySelector('#myContent').style.backgroundColor = 'blue';
`;
export default class App extends Component {
render() {
return (
<View style={localStyles.viroContainer}>
<WebView
source={{ html: "<h1 id='myContent'>Hello</h1>" }}
style={{ flex: 1 }}
ref={webview => {this.myWebview = webview;}}
injectedJavaScript={jsCode}
javaScriptEnabled={true}
/>
<TouchableHighlight
style={localStyles.overlayButton}
onPress={this.sendMessageToWebView2}
underlayColor="transparent">
<Text>Send message to WebView</Text>
</TouchableHighlight>
</View>
);
}
sendMessageToWebView2 = () => {
console.log(this.myWebview);
console.log(this);
this.myWebview.injectJavaScript(`
(function () {
document.querySelector('body').style.backgroundColor = 'orange';
})();
`);
};
}
var localStyles = StyleSheet.create({
viroContainer: {
flex: 1,
},
overlayButton: {
position: 'absolute',
bottom: 0,
left: 110,
height: 50,
width: 150,
paddingTop: 30,
paddingBottom: 30,
marginTop: 10,
marginBottom: 10,
backgroundColor: '#f0a0aa',
borderRadius: 10,
borderWidth: 2,
borderColor: '#000',
},
});

Load a local image after loading a remote image failed

Is it possible to load a local image if the remote image failed?
For example, I have the following code:
<Image style={ styles.userImage }
source={ { uri: http://example.com/my_image.jpg } }
onError={(error) => ...}
/>
In case for example I don't have the rights to access http://example.com/my_image.jpg, I'll get an error in onError. Is there a way then to load a local image instead?
Use component' state. In your constructor set initial url:
this.state = { image: { uri: 'http://example.com/my_image.jpg' } }
Create onError handler:
onError(error){
this.setState({ image: require('your_local_image.path')})
}
And then combine it all together:
<Image style={ styles.userImage }
source={ this.state.image }
onError={ this.onError.bind(this) }
/>
As per the latest docs you can use defaultSource property. It shows the image till the original image loads, if the load fails the default image is shown Link to docs
To elaborate on Cherniv's answer you could create an <Images /> component that abstracts this away for you:
import React from 'react';
import { Image } from 'react-native';
export default class Images extends React.Component {
static defaultProps = {
source: [],
onError: () => {},
}
state = { current: 0 }
onError = error => {
this.props.onError(error);
const next = this.state.current + 1;
if (next < this.props.source.length) {
this.setState({ current: next });
}
}
render() {
const { onError, source, ...rest } = this.props;
return (
<Image
source={source[this.state.current]}
onError={this.onError}
{...rest}
/>
);
}
}
Then you can use it like this:
import Images from './Images';
<Images
source={[
{ uri: 'http://example.com/bad_image.jpg' },
{ uri: 'http://example.com/good_image.jpg' },
require('./default.jpg'),
]}
style={{
backgroundColor: '#ccc',
height: 200,
width: 200,
}}
/>
Create a component ImageLoad like this:
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import { Image, ImageBackground, ActivityIndicator, View } from 'react-native';
class ImageLoad extends PureComponent {
static propTypes = {
isShowActivity: PropTypes.bool,
};
static defaultProps = {
isShowActivity: true,
};
constructor(props) {
super(props);
this.state = {
isLoaded: false,
isError: false
};
}
onLoadEnd(){
this.setState({
isLoaded: true
});
}
onError(){
this.setState({
isError: true
});
}
render() {
const {
style, source, resizeMode, borderRadius, backgroundColor, children,
loadingStyle, placeholderSource, placeholderStyle,
customImagePlaceholderDefaultStyle
} = this.props;
return(
<ImageBackground
onLoadEnd={this.onLoadEnd.bind(this)}
onError={this.onError.bind(this)}
style={[styles.backgroundImage, style]}
source={source}
resizeMode={resizeMode}
borderRadius={borderRadius}
>
{
(this.state.isLoaded && !this.state.isError) ? children :
<View
style={[styles.viewImageStyles, { borderRadius: borderRadius }, backgroundColor ? { backgroundColor: backgroundColor } : {}]}
>
{
(this.props.isShowActivity && !this.state.isError) &&
<ActivityIndicator
style={styles.activityIndicator}
size={loadingStyle ? loadingStyle.size : 'small'}
color={loadingStyle ? loadingStyle.color : 'gray'}
/>
}
<Image
style={placeholderStyle ? placeholderStyle : [styles.imagePlaceholderStyles, customImagePlaceholderDefaultStyle]}
source={placeholderSource ? placeholderSource : require('./Images/empty-image.png')}
>
</Image>
</View>
}
{
this.props.children &&
<View style={styles.viewChildrenStyles}>
{
this.props.children
}
</View>
}
</ImageBackground>
);
}
}
const styles = {
backgroundImage: {
position: 'relative',
},
activityIndicator: {
position: 'absolute',
margin: 'auto',
zIndex: 9,
},
viewImageStyles: {
flex: 1,
backgroundColor: '#e9eef1',
justifyContent: 'center',
alignItems: 'center'
},
imagePlaceholderStyles: {
width: 100,
height: 100,
resizeMode: 'contain',
justifyContent: 'center',
alignItems: 'center'
},
viewChildrenStyles: {
top: 0,
left: 0,
right: 0,
bottom: 0,
position: 'absolute',
backgroundColor: 'transparent'
}
}
export default ImageLoad;
and use this anywhere in your app:
<ImageLoad
style={{ width: 320, height: 250 }}
loadingStyle={{ size: 'large', color: 'blue' }}
source={{ uri: 'url image' }}
/>

How to refactoring react components with member variable to es6 classes

How can I refactor React components with a member variable to es6 classes
It works without state variable. Why, when running the code below, do I get a big red screen with "Can't add property counter, object is not extensible"?
'use strict';
let Dimensions = require('Dimensions');
let totalWidth = Dimensions.get('window').width;
let leftStartPoint = totalWidth * 0.1;
let componentWidth = totalWidth * 0.8;
import React, {
AppRegistry,
Component,
StyleSheet,
Text,
TextInput,
View
} from 'react-native';
class Login extends Component {
constructor(props) {
super(props);
this.counter =23;
this.state = {
inputedNum: ''
};
}
updateNum(aEvent) {
this.setState((state) => {
return {
inputedNum: aEvent.nativeEvent.text,
};
});
}
buttonPressed() {
this.counter++;
console.log(':::'+this.counter);
}
render() {
return (
<View style={styles.container}>
<TextInput style={styles.numberInputStyle}
placeholder={'input phone number'}
onChange={(event) => this.updateNum(event)}/>
<Text style={styles.bigTextPrompt}
onPress={this.buttonPressed}>
Press me...
</Text>
</View>
);
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
numberInputStyle: {
top: 20,
left: leftStartPoint,
width: componentWidth,
backgroundColor: 'gray',
fontSize: 20
},
bigTextPrompt: {
top: 70,
left: leftStartPoint,
width: componentWidth,
backgroundColor: 'gray',
color: 'white',
textAlign: 'center',
fontSize: 60
}
});
AppRegistry.registerComponent('Project18', () => Login);
You need to set up the value in the constructor:
constructor(props) {
super(props)
this.counter = 23
}
You may be receiving the errors because of the way the state is being set. Try setting the state like this:
updateNum(aEvent) {
this.setState({
inputedNum: aEvent.nativeEvent.text,
})
}
And the onPress function should be called like this:
<Text style={styles.bigTextPrompt} onPress={() => this.buttonPressed()}>
I've set up a full working project here also pasted the code below.
https://rnplay.org/apps/Se8X5A
'use strict';
import React, {
AppRegistry,
Component,
StyleSheet,
Text,
TextInput,
View,
Dimensions
} from 'react-native';
let totalWidth = Dimensions.get('window').width;
let leftStartPoint = totalWidth * 0.1;
let componentWidth = totalWidth * 0.8;
class SampleApp extends Component {
constructor(props) {
super(props);
this.counter =23;
this.state = {
inputedNum: ''
};
}
updateNum(aEvent) {
this.setState({
inputedNum: aEvent.nativeEvent.text,
})
}
buttonPressed() {
this.counter++;
console.log(':::'+this.counter);
}
render() {
return (
<View style={styles.container}>
<TextInput style={styles.numberInputStyle}
placeholder={'input phone number'}
onChange={(event) => this.updateNum(event)}/>
<Text style={styles.bigTextPrompt}
onPress={() => this.buttonPressed()}>
Press me...
</Text>
</View>
);
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
numberInputStyle: {
top: 20,
left: leftStartPoint,
width: componentWidth,
backgroundColor: 'gray',
fontSize: 20,
width:200,
height:50
},
bigTextPrompt: {
top: 70,
left: leftStartPoint,
width: componentWidth,
backgroundColor: 'gray',
color: 'white',
textAlign: 'center',
fontSize: 60
}
});
AppRegistry.registerComponent('SampleApp', () => SampleApp);