onPress is called automatically while using NetInfo - react native - react-native

NetInfo is used to check the connection & theres a checkNetwork function called in onPress of TouchableOpacity. When the button is clicked once, the checkNetwork
function is called automatically multiple times without tapping the button when the network connection is lost and vice versa.
Please have a look at the code here:
Please have a look at the video
export default class App extends React.Component {
checkNetwork = () => {
NetInfo.addEventListener((state) => {
if (state.isConnected) {
alert('isConnected');
} else {
alert('not connected');
}
});
};
render() {
return (
<View style={styles.container}>
<TouchableOpacity
activeOpacity={0.8}
onPress={()=> this.checkNetwork()}>
<Text>Check here</Text>
</TouchableOpacity>
</View>
);
}
}

You should not declare event listener inside of the click itself,
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {alert: ''};
}
componentDidMount() {
NetInfo.addEventListener((state) => this.setState({ alert: state.isConnected ? 'isConnected' : 'not connected'})
}
checkNetwork = () => alert(this.state.alert)
render() {
return (
<View style={styles.container}>
<TouchableOpacity
activeOpacity={0.8}
onPress={()=> this.checkNetwork()}>
<Text>Check here</Text>
</TouchableOpacity>
</View>
);
}
}
though in your case event listener isn't exactly the fit logic for UI behavior of pressing button, so I think you might wanna use useNetInfo
import {useNetInfo} from "#react-native-community/netinfo";
class App extends React.Component {
checkNetwork = () => {
if (this.props.netInfo.isConnected) {
alert('isConnected');
} else {
alert('not connected');
}
});
};
...rest render...
}
export default () => <App netInfo={useNetInfo()} />

Related

How to separate axios call from component in react native?

I am new to react native. I have following component in my project for now I have written for fetching API in same component but want to separate it out. I am getting difficulty for how can i access variable which I am using in "getAlbum" method from outside of component.
Is there standard way to separate API call from component?
import React, { Component } from 'react';
import {
FlatList, Text, View, Image, TouchableOpacity,
} from 'react-native';
import { ActivityIndicator, Provider } from 'react-native-paper';
import axios from 'axios';
import styles from '../style/ThumbnailView.component.style';
import ErrorAlert from '../common/ErrorAlert';
import * as myConstant from '../common/Constants';
export default class HomeScreen extends Component {
// For to Navigation header
static navigationOptions = () => ({
headerTitle: 'Album Information',
});
constructor(props) {
super(props);
this.state = {
isLoading: true,
apiLoadingError: false,
};
}
getAlbums() {
const { navigation } = this.props;
const albumId = navigation.getParam('albumID', 'no data');
axios
.get(
myConstant.API + `photos?albumId=${albumId}`, {timeout: myConstant.TIMEOUT}
)
.then((response) => {
this.setState({
isLoading: false,
dataSource: response.data,
});
})
.catch(err => {
this.setState({isLoading: false, apiLoadingError: true})
});
}
componentDidMount() {
this.getAlbums();
}
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 30 }}>
<ActivityIndicator animating={true} size='large' />
</View>
);
}
if (this.state.apiLoadingError) {
return (
<ErrorAlert />
);
}
return (
<React.Fragment>
<Provider>
<View style={styles.listContainer} >
<FlatList
testID='flatlist'
data={ this.state.dataSource } numColumns={3}
renderItem={({ item }) => <View style={styles.listRowContainer}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('AlbumDetailsViewScreen', {
albumTitle: item.title, albumImg: item.url
})} style={styles.listRow}>
<View style={styles.listTextNavVIew}>
<Image source = {{ uri: item.thumbnailUrl }} style={styles.imageViewContainer} />
</View>
</TouchableOpacity>
</View>
}
keyExtractor = { (item, index) => index.toString() }
/>
</View>
</Provider>
</React.Fragment>
);
}
}
You can separate your axios call by making another class with function which will receive 'albumID' as an argument - then add it to your axios link. If you want to call this function from another class just make it static and use like in example below. Then you can map your fetchData to parse it into state. Hope it will help you.
export class Api {
static fetchData = (albumId: string) => {
//here your axios call which will return an array
}
}
export default class HomeScreen extends React.Component {
state = {
//.....
}
receivedData = Api.fetchData('albumID');
//you can map array here to get what you want.
}

Update props from other component in react native

I have a Main class which I show an array to user, then in detail page user can edit each element which I'm passing using react navigation parameter. I want to edit my array in the detail class and save it using async storage.
//Main.jsimport React from 'react';
import {
StyleSheet ,
Text,
View,
TextInput,
ScrollView,
TouchableOpacity,
KeyboardAvoidingView,
AsyncStorage
} from 'react-native'
import Note from './Note'
import detail from './Details'
import { createStackNavigator, createAppContainer } from "react-navigation";
export default class Main extends React.Component {
static navigationOptions = {
title: 'To do list',
headerStyle: {
backgroundColor: '#f4511e',
},
};
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
dueDate: ''
};
}
async saveUserTasks(value) {
try {
await AsyncStorage.setItem('#MySuperStore:userTask',JSON.stringify(value));
} catch (error) {
console.log("Error saving data" + error);
}
}
getUserTasks = async() =>{
try {
const value = await AsyncStorage.getItem('#MySuperStore:userTask');
if (value !== null){
this.setState({ noteArray: JSON.parse(value)});
}
} catch (error) {
console.log("Error retrieving data" + error);
}
}
render() {
this.getUserTasks()
let notes = this.state.noteArray.map((val,key) => {
return <Note key={key} keyval={key} val={val}
deleteMethod={ () => this.deleteNote(key)}
goToDetailPage= {() => this.goToNoteDetail(key)}
/>
});
const { navigation } = this.props;
return(
<KeyboardAvoidingView behavior='padding' style={styles.keyboard}>
<View style={styles.container}>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<View style={styles.footer}>
<TextInput
onChangeText={(noteText) => this.setState({noteText})}
style={styles.textInput}
placeholder='What is your next Task?'
placeholderTextColor='white'
underlineColorAndroid = 'transparent'
>
</TextInput>
</View>
<TouchableOpacity onPress={this.addNote.bind(this)} style={styles.addButton}>
<Text style={styles.addButtonText}> + </Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
addNote(){
if (this.state.noteText){
var d = new Date();
this.state.noteArray.push({
'creationDate': d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDay(), 'taskName': this.state.noteText,'dueDate':'YYYY/MM/DD'
});
this.setState({noteArray:this.state.noteArray})
this.setState({noteText: ''});
this.saveUserTasks(this.state.noteArray)
}
}
deleteNote(key){
this.state.noteArray.splice(key,1);
this.setState({noteArray: this.state.noteArray})
this.saveUserTasks(this.state.noteArray)
}
goToNoteDetail=(key)=>{
this.props.navigation.navigate('DetailsScreen', {
selectedTask: this.state.noteArray[key],
});
}
}
in detail view I have this method which is similar to add note in main class:
export default class Details extends React.Component {
render() {
const { navigation } = this.props;
const selectedTask = navigation.getParam('selectedTask', 'task');
return(
<View key={this.props.keyval} style={styles.container}>
<TouchableOpacity onPress={this.saveEdit.bind(this)} style={styles.saveButton}>
<Text style={styles.saveButtonText}> save </Text>
</TouchableOpacity>
</View>
);
}
saveEdit(){
let selectedItem = { 'creationDate': selectedTask['creationDate'],
'taskName': selectedTask['taskName'],
'dueDate': this.state.dueData}
this.props.navigation.state.params.saveEdit(selectedItem)
}
}
How can I change my props in any component?
First of all you shouldn't call this.getUserTasks() in the render method because the function has this.setState which is bad and could end in a endless loop I guess or at least effect in worse performance. You could instead call it in componentDidMount:
componentDidMount = () => {
this.getUserTasks();
}
Or alternatively call already in constructor but I prefer the first option:
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
dueDate: ''
};
this.getUserTasks()
}
this.props.noteArray.push({.. is probably undefined because you aren't passing it down any where. (Didn't see any reference in your snippet). I guess I would implement the saveEdit function in the Main.js component and simply pass it down to the navigation route and call the function in Details component by accessing the navigation state props:
Update
goToNoteDetail=(key)=>{
this.props.navigation.navigate('DetailsScreen', {
// selectedTask: this.state.noteArray[key],
selectedItem: key,
saveEdit: this.saveEdit
});
}
saveEdit(selectedItem){
const selectedTask = this.state.noteArray[selectedItem]
this.state.noteArray.push({
'creationDate': selectedTask['creationDate'],
'taskName': selectedTask['taskName'],
'dueDate': this.state.dueData
});
this.setState({noteArray:this.state.noteArray})
this.setState({dueData: 'YYYY/MM/DD'});
this.saveUserTasks(this.state.noteArray)
}
And then call saveEdit in Details Component:
saveSelectedItem = () => {
const { navigation } = this.props.navigation;
const {selectedItem, saveEdit} = navigation.state && navigation.state.params;
saveEdit(selectedItem)
}

Cannot update during existing state

Index.js
I try do launch screen.
export default class LaunchScreen extends Component{
constructor(props){
super(props);
this.state= {
loaded:false
}
}
componentWillMount(){
Thread.load(v => this.setState({loaded:true}));
}
render(){
const { navigate } = this.props.navigation;
return(
<View style={styles.container}>
{this.state.loaded ? navigate("Form")
:
<View style={styles.imageContent}>
<Image style={styles.image}
source={require('../images/launch_icon.png')}/>
</View>
}
</View>
)}}
export default class thread{
static load(cb){
setTimeout(cb,3000);
}
}
when I use these codes I get the warning "can not update during an existing state transition". How to fix it?
You're trying to navigate inside a return, try to change your render code to this:
render(){
const { navigate } = this.props.navigation;
if(this.state.loaded) navigate("Form")
return(
<View style={styles.container}>
<View style={styles.imageContent}>
<Image style={styles.image}
source={require('../images/launch_icon.png')}/>
</View>
</View>
)
}
EDIT: But you should probably do the navigation part inside a shouldComponentUpdate() checking if the nextState.loaded is different from your this.state.loaded and is true, like:
shouldComponentUpdate(nextProps, nextState){
if((nextState.loaded!=this.state.loaded)&&nextState.loaded){
navigate("Form")
return true
}
return false
}
I see a couple problems, but I don't think these are your main problems.
You can only have one default export, so if both classes are actually in a single file, remove the "default" from the thread class.
Pretty sure all class names must begin with a capital letter in React, so change "thread" to "Thread"
But I think your actual problem is that you're calling navigate('Form') directly from within the render. Try adding a handleNavigation class method and calling this.handleNavigation there instead. So you'd be left with something like this...
export default class LaunchScreen extends Component {
state = {
loaded: false
}
componentWillMount() {
Thread.load( () => this.setState({loaded: true}));
}
handleNavigation = () => {
this.props.navigation('Form');
}
render() {
return (
<View style={styles.container}>
{
this.state.loaded
? this.handleNavigation
: <View style={styles.imageContent}>
<Image style={styles.image}
source={require('../images/launch_icon.png')}/>
</View>
}
</View>
);
}
}
export class Thread {
static load(cb){
setTimeout(cb,3000);
}
}

Sharing strings to other native applications not working in react native

I have been following this tutorial, however, when I go to run the application after dropping the code in, it doesn't seem to work. The tutorial seems fairly simple, so I can not understand why it is not working.
The error message I'm getting is:
import React from 'react'
import { View, Text, StyleSheet, Image, Share } from 'react-native'
class ShareLesson extends Component {
constructor(props) {
super(props);
this._shareMessage = this._shareMessage.bind(this);
this._showResult = this._showResult.bind(this);
this.state = { result: ''};
}
_showResult(result) {
this.setState({result});
}
_shareMessage() {
Share.share({
message: 'This is a simple shared message'
}).then(this._showResult);
}
render() {
return (
<View style={styles.container}>
<TouchableHighlight onPress={this._shareMessage}>
<Text style={styles.welcome}>
Share
</Text>
</TouchableHighlight>
<Text>
{JSON.stringify(this.state.result)}
</Text>
<View>);
}
}
Tabs Component
class Tabs extends Component {
_changeTab (i) {
const { changeTab } = this.props
changeTab(i)
}
_renderTabContent (key) {
switch (key) {
case 'today':
return <Home />
case 'share':
return <Share />
case 'savequote':
return <SaveQuote />
case 'moremenu':
return <MoreMenu />
}
}
render () {
const tabs = this.props.tabs.tabs.map((tab, i) => {
return (
<TabBarIOS.Item key={tab.key}
icon={tab.icon}
selectedIcon={tab.selectedIcon}
title={tab.title}
onPress={() => this._changeTab(i)}
selected={this.props.tabs.index === i}>
{this._renderTabContent(tab.key)}
</TabBarIOS.Item>
)
})
return (
<TabBarIOS tintColor='black'>
{tabs}
</TabBarIOS>
)
}
}
export default Tabs

Infinite loop with React-Redux and Navigation Experimental

I have an infinite loop that seems to be occurring when I use react-redux. I use Navigation Experimental which loads connectRouteScreen as the scene to be rendered through NavigationCardStack. I'm using RN 0.30. But also could reproduce this in 0.31-rc.0
[...]
Use Navigation Experimental to transition and load connectRouteScreen as a Scene
export default function connectRouteScreen(Scene, sceneProps){
class RouteScreen extends React.Component{
[...]
render() {
const { navigator, pathVariables } = this.props;
return (
<View style={styles.container}>
<Scene
navigator={navigator}
{...pathVariables.toJS()}
/>
</View>);
}
}
RouteScreen.propTypes = {...RouteScreenPropTypes};
const routeScreenProperties = extractSceneRendererProps(sceneProps);
/*return <Scene
navigator={routeScreenProperties.navigator}
{...routeScreenProperties.pathVariables.toJS()}
/>;
*/
return <RouteScreen
{...routeScreenProperties}
/>;
}
LoadingScreen is loaded as "Scene".
#connect(
() => {return {}},
(dispatch) => {
return {
loginActions: bindActionCreators(loginActions, dispatch),
}
})
export default class LoadingScreen extends React.Component {
constructor(props){
super(props);
}
shouldComponentUpdate(nextProps){
return false;
}
componentDidMount(){
const { navigator } = this.props;
this.props.loginActions.executeLoginFlow();
}
render() {
const Animatable = require('react-native-animatable');
return (
<Animatable.View
animation="pulse"
easing="ease-out"
iterationCount="infinite"
style={localStyle.container}>
<Icon name="logo" style={localStyle.iconStyle} size={150}/>
</Animatable.View>
);
}
};
So, If I return the Scene directly instead of RouteScreen, no problem.
If I remove the #connect syntax and escape this.props.loginActions..., no problem.
If I return RouteScreen and remove everything it does and just return the Scene => infinite loop.
Does anybody have any suggestions how to deal with this?