How to clear interval when app in background state react-native? - reactjs

I have a component which fetch a request in every 30 seconds interval, it's working fine as expected. But I want when app goes in background state the request will stop and when app in foreground the request will start and vise-versa. Is there is any solution to do this.
Here is my component file.
Notifications.js
/**
* Notification Button
*/
import React from "react";
import { View, TouchableWithoutFeedback, Text } from "react-native";
import { connect } from "react-redux";
import Icon from 'react-native-vector-icons/MaterialIcons';
// styles
import styles from "./Styles";
// global styles
import globalStyles from "BkProvider/src/styles/global";
import { colors } from "BkProvider/src/styles/base";
// root navigator
import { rootNavigator } from "BkProvider/src/screens/DashboardGroup";
// strings
import strings from "BkProvider/src/strings";
// actions
import { getSystemAlerts, seeAllSystemAlertLogs, stopNotificationTick } from "BkProvider/src/actions";
// Navigation constants
import NavigationConstants from "BkProvider/src/constants/NavigationConstants";
const { posRelative } = globalStyles;
class NotificationButton extends React.Component {
componentDidMount() {
this.props.getSystemAlerts();
}
componentWillUnmount() {
stopNotificationTick();
}
/**
* Function to open system alerts popup
*/
openSystemAlerts() {
this.props.seeAllSystemAlertLogs(this.props.unseenAlertLogIdsArray);
rootNavigator.push({
screen: NavigationConstants.SYSTEM_ALERTS_LISTING,
title: strings.allNotifications,
backButtonTitle: ''
});
}
render() {
const { unseenAlertLogIdsArray } = this.props;
return (
<TouchableWithoutFeedback onPress={this.openSystemAlerts.bind(this)}>
<View style={styles.button}>
<View style={[posRelative]}>
<Icon
name="notifications-none"
size={27}
color={colors.white}
/>
{(unseenAlertLogIdsArray && unseenAlertLogIdsArray.length > 0) &&
<Text style={styles.badge}>{unseenAlertLogIdsArray.length}</Text>
}
</View>
</View>
</TouchableWithoutFeedback>
);
}
}
const mapStateToProps = ({ systemAlerts }) => {
const { unseenAlertLogIdsArray } = systemAlerts;
return { unseenAlertLogIdsArray }
}
export default connect(mapStateToProps, {
getSystemAlerts,
seeAllSystemAlertLogs
})(NotificationButton);
Actions.js
/**
* System Alerts Actions
*/
import Toast from "react-native-simple-toast";
import { NetInfo } from "react-native";
// action types
import {
GET_SYSTEM_ALERTS,
SEE_ALL_SYSTEM_ALERT_LOGS
} from "BkProvider/src/actions/actionTypes";
// helpers methods
import { getUserId } from "./AppInitializer";
// webservice config
import WebServiceConfig, { APITypes } from "../webservice/WebServiceConfig";
import WebService from "../webservice/WebService";
import APINames from "../webservice/APINames";
let timer = null;
let globalDispatch;
let webServiceObject
/**
* Function To Get System Alerts
*/
export const getSystemAlerts = () => (dispatch) => {
clearInterval(timer);
globalDispatch = dispatch;
let apiConfig = new WebServiceConfig(APINames.LoadSystemAlertLogs),
httpParams = {
'page': "",
'limit': "",
'role': 'provider',
'uid': getUserId()
}
webServiceObject = new WebService(onResultCallback)
.addPostParameterObject(httpParams)
.addServiceConfiguration(apiConfig)
timer = setInterval(() => notificationsTick(), 15000);
notificationsTick();
}
/**
* API Response Callback Handler
*/
const onResultCallback = (webServiceResultObj) => {
if (webServiceResultObj.isSuccess && webServiceResultObj.response != null) {
if (webServiceResultObj.response.api_status === 1) {
if (globalDispatch) {
globalDispatch({ type: GET_SYSTEM_ALERTS, payload: webServiceResultObj.response.data });
}
}
} else {
if (webServiceResultObj.shouldShowErrorMessage)
Toast.show(webServiceResultObj.errorMessage)
}
}
/**
* System Alerts Notification Ticks
*/
const notificationsTick = () => {
NetInfo.isConnected.fetch().then(isConnected => {
if (isConnected) {
if (webServiceObject)
webServiceObject.execute();
}
})
}
/**
* Function To Clear The Interval Of System Alerts Api
*/
export const stopNotificationTick = () => {
clearInterval(timer);
}
/**
* Function To See All System Alerts Logs
*/
export const seeAllSystemAlertLogs = (unseenAlertLogIdsArray) => (dispatch) => {
if (unseenAlertLogIdsArray) {
let apiConfig = new WebServiceConfig(APINames.SeeAllSystemAlertLogs)
.setAPIType(APITypes.POST);
// params
let params = {
role: "provider",
uid: getUserId(),
unseen_log_ids: unseenAlertLogIdsArray
}
dispatch({ type: SEE_ALL_SYSTEM_ALERT_LOGS }); // dispatch an action to see all system alert logs
new WebService()
.addPostParameterObject(JSON.stringify(params))
.addServiceConfiguration(apiConfig)
.execute()
}
}

Just check the AppState when notificationsTick gets called.
if(AppState.currentState === "active"){
notificationsTick();
}
Note that JS timers might not get called when your app is in background - From my own experience, on iOS, click the home button, touch power button to enter sleep mode, application Javascript gets frozen in 10 seconds. And they might get called when your app gets back in foreground... You should test that
A more robust implementation would keep a reference on the timer, and cancel it when app goes into background.

You should use App state for this case here is the sample code that i used
import React, {Component} from 'react'
import {AppState, Text} from 'react-native'
class AppStateExample extends Component {
state = {
appState: AppState.currentState
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = (nextAppState) => {
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
console.log('App has come to the foreground!')
}
this.setState({appState: nextAppState});
}
render() {
return (
<Text>Current state is: {this.state.appState}</Text>
);
}
}

There is a React native API for that. https://facebook.github.io/react-native/docs/appstate
This would let you check if the app is in background or foreground, and then you can either cancel the request, or continue.
Basic Usage example from their Docs:
import React, {Component} from 'react'
import {AppState, Text} from 'react-native'
class AppStateExample extends Component {
state = {
appState: AppState.currentState
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = (nextAppState) => {
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
console.log('App has come to the foreground!')
}
this.setState({appState: nextAppState});
}
render() {
return (
<Text>Current state is: {this.state.appState}</Text>
);
}
}
This console logs the current state of the app, so you would need to add this AppState to your state and do whatever you need.

Related

How to go back to navigate the previous screen without re rendering any function react

Here on button click event I am uploading some data in server . I am using mutation for this . And after the response I have to navigate to previous screen and I don't want to refresh the page or re-render the any life cycle methods of the screen where i am directing .
I have used this.props.navigation.navigate("pagename") ,but by using this some function is getting call .
So I have used "this.props.navigation.goBack()" ,but again same .
I have to go back to previous screen after submitting request to server.
import React, { Component } from 'react';
import { View } from 'native-base';
import {withApollo } from 'react-apollo';
import gql from 'graphql-tag';
import _ from 'lodash';
import OverlaySpinner from '../ui/overlaySpinner';
import AddNoteSection from '../../components/tabs/requestTab/AddNoteSection';
import { handleErrors } from '../../services';
class AddNoteSectionContainer extends Component {
constructor(props) {
super(props);
this.state = {
categoryList: [],
isOpenClose: false,
notes: "",
notesResponse:[]
};
}
addNoteChange = (event) => {
this.setState({
notes: event
}, () => {
});
};
statusTextModification = (currentstatus) => {
var status ="";
if (currentstatus === "Pending"){
status = "P"
}else if(currentstatus === "Close"){
status = "C"
}else{
status = "A"
}
return status;
}
OnButtonClick = async (data) => {
var status = "";
const{navigation}=this.props;
const{workFlowDetails,troubleTicketDetails} =
navigation.state.params.ticketDetailsInfo;
const workAgent_ID = workFlowDetails.currentNextActivity;
const currentStepPosition = workAgent_ID.filter((item) => {
return item._activityStatus === "I"
});
const workAgentID = currentStepPosition.map(currentStepPosition => {
return currentStepPosition._workAgent;
});
let workAgent_id=workAgentID[0];
console.log("Props for note notes",workAgent_id);
if (navigation.state.params.currentStatus === "Pending"){
status = "P"
}else if(navigation.state.params.currentStatus === "Close"){
status = "C"
}else{
status = "A"
}
const mutationObj = `
mutation createIncidentNote{
createIncidentNote(
input:{
status: "${status}",
incidentInitiator: "${data}",
notes: "${this.state.notes}",
userId: "${troubleTicketDetails.LoggedBy}",
workAgentID: "${workAgent_id}",
referenceNumber: "${navigation.state.params.referenceNumber}",
}){
REQUEST_STATUS
ABILLITY_REF_NUM
SUCCESS_MESG_LANG_1
SUCCESS_MESG_LANG_2
}
}
`;
try {
const { data } = await this.props.client.mutate({
mutation: gql(mutationObj)
});
// Here below is the code I am using .
this.props.navigation.goBack()
} catch (e) {
handleErrors(e, this.props.navigation);
console.log('Error in Adding note', e);
}
};
render(){
return(
<View>
<AddNoteSection
{...this.props}
addNoteChange={(text) => this.addNoteChange(text)}
OnButtonClick={(data) => this.OnButtonClick(data)}
/>
{/* {<OverlaySpinner color="#00678F" />} */}
</View>
)
}
}
export default withApollo(AddNoteSectionContainer);

React Native global back handling

I have 3 components:
ComponentA
ComponentB
BackPressHandlingComponent
BackPressHandlingComponent deals with back press.
When back pressed from ComponentA; I must exit the app.
When back pressed from ComponentB; I must go to ComponentA.
Here is my BackPressHandlingComponent code -
import { BackHandler } from 'react-native';
export class BackPressHandlingComponent extends Component {
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
}
}
My question is -
How do I tell BackPressHandlingComponent from Component A that I must exit app and from Component B that I need to go back to Component A
As per your use case, I would have addedBackpress event listeners on ComponentA and ComponentB, such that when you are on ComponentA when the callback is called you can exit the app and when in ComponentB its callback is called you can navigate to ComponentA.
Simple demo for above solution:
App.js
/**
*
* #format
* #flow
*/
import React, { Component } from 'react';
import { Text, View, TouchableOpacity, StyleSheet } from 'react-native';
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'
import BackHandlerHOC from './BackHandlerHOC'
type Props = {};
export default class App extends Component<Props> {
state = {
render: 'A'
}
toggleComponent = () => {
let component = 'A'
if (this.state.render === 'A') {
component = 'B'
}
this.setState({ render: component })
}
render() {
const { render } = this.state
const wrappercomponent = render === 'A' ? (
<BackHandlerHOC
name="ComponentA"
Component={ComponentA}
/>
) : (
<BackHandlerHOC
name="ComponentB"
Component={ComponentB}
/>
)
return (
<View style={styles.container}>
<TouchableOpacity
onPress={() => this.toggleComponent()}
>
<Text> Change </Text>
</TouchableOpacity>
{wrappercomponent}
</View>
)
}
}
const styles = StyleSheet.create({
container: {
padding: 20
}
})
ComponentA
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class ComponentA extends Component {
render() {
return (
<View>
<Text>A</Text>
</View>
);
}
}
export default ComponentA;
ComponentB
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class ComponentB extends Component {
render() {
return (
<View>
<Text>B</Text>
</View>
);
}
}
export default ComponentB;
BackHandlerHOC
import React, { Component } from 'react';
import { BackHandler, ToastAndroid, View, Text } from 'react-native';
class BackHandlerHOC extends Component {
componentDidMount = () => {
BackHandler.addEventListener('hardwareBackPress', this.backPressHandler);
};
componentWillUnmount = () => {
BackHandler.removeEventListener('hardwareBackPress', this.backPressHandler);
};
backPressHandler = () => {
const { name } = this.props;
if (name === 'ComponentA') {
BackHandler.exitApp()
} else {
// this.props.navigator.resetTo({
// screen: 'ComponentA'
// })
ToastAndroid.show('will go back to A', 0);
}
return true;
};
render() {
const { Component } = this.props;
return (
<View>
<Text>Hello from backpress</Text>
<Component />
</View>
);
}
}
export default BackHandlerHOC;
You can also find the working example on expo here
Hope this helps
Just to add another approach,
I made use of the react-navigation lifecycle events,and the hardwareBackPress event, mind you the version of react-navigation here is 3.x.x.
The lifecycle event onWillFocus is called when the screen comes in view and the life-cycle event onWillBlur is called when the user is moving on to another screen, here somehow the React lifecycle events are in the hands of react-navigation, hence cannot use them here see https://reactnavigation.org/docs/3.x/navigation-lifecycle.
Following is the code:
import { BackHandler,Alert } from "react-native";
import { NavigationEvents } from 'react-navigation';
class SomeComponent {
//...my componentDidMount etc and other methods.....
backButtonAction(){
Alert.alert(
"Confirm Exit",
"Do you want to exit the app?",
[
{
text: "No",
onPress: () => {},
style: "cancel"
},
{ text: "Yes", onPress: () => BackHandler.exitApp() }
],
{ cancelable: false }
);
return true; // coz the event handler needs to return boolean.
};
setBackButtonAction(){
BackHandler.addEventListener(
"hardwareBackPress",
this.backButtonAction
);
}
removeBackButtonAction(){
BackHandler.removeEventListener(
"hardwareBackPress",
this.backButtonAction
);
}
render() {
return (
<Container>
<NavigationEvents
onWillFocus={payload => this.setBackButtonAction()}
onWillBlur={payload => this.removeBackButtonAction()}
/> //..... my view code
</Container>)
}
}

How to send automatic constant message in web app based on reactjs?

This is my App.js and i have set my database in firebase. All the messages which i enter all display in database also.But i need to automatically send message back to me . so any one knows how to do that please help. Thank you.
import React, { Component } from 'react';
import MessagePane from './MessagePane';
import ChannelList from './ChannelList';
import { getMessages, getChannels, saveMessage, onNewMessage } from './remote_storage1';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
messages: [],
channels: [],
selected_channel_id: null
};
this.onSendMessage = this.onSendMessage.bind(this);
this.onChannelSelect = this.onChannelSelect.bind(this);
this.filteredMessages = this.filteredMessages.bind(this);
}
componentDidMount() {
getMessages().then(messages => this.setState({messages}));
getChannels().then(channels => this.setState({channels, selected_channel_id: channels[0].id}));
onNewMessage(new_message => {
const messages = [...this.state.messages, new_message];
this.setState({messages});
});
}
onSendMessage(author, text) {
const new_message = {
id: this.state.messages[this.state.messages.length - 1].id + 1,
author,
text,
channel_id: this.state.selected_channel_id
};
saveMessage(new_message);
const messages = [...this.state.messages, new_message];
this.setState({messages});
}
onChannelSelect(id) {
this.setState({ selected_channel_id: id });
}
filteredMessages() {
return this.state.messages.filter(({channel_id}) => channel_id === this.state.selected_channel_id);
}
render() {
return (
<div className="App">
<ChannelList
channels={this.state.channels}
selectedChannelId={this.state.selected_channel_id}
onSelect={this.onChannelSelect}
/>
<MessagePane messages={this.filteredMessages()} onSendMessage={this.onSendMessage} />
</div>
);
}
}
export default App;

how can i create .env file in firebase for a react chat web app?

import React, { Component } from 'react';
import MessagePane from './MessagePane';
import ChannelList from './ChannelList';
import { getMessages, getChannels, saveMessage, onNewMessage } from './storage';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {
messages: [],
channels: [],
selected_channel_id: null
};
this.onSendMessage = this.onSendMessage.bind(this);
this.onChannelSelect = this.onChannelSelect.bind(this);
}
componentDidMount() {
getMessages().then(messages => this.setState({messages}));
getChannels().then(channels => this.setState({channels, selected_channel_id: channels[0].id}));
onNewMessage(new_message => {
const messages = [...this.state.messages, new_message];
this.setState({messages});
});
}
onSendMessage(author, text) {
const new_message = {
id: this.state.messages[this.state.messages.length - 1].id + 1,
author,
text,
channel_id: this.state.selected_channel_id
};
saveMessage(new_message);
const messages = [...this.state.messages, new_message];
this.setState({messages});
}
onChannelSelect(id) {
this.setState({ selected_channel_id: id });
}
filteredMessages() {
return this.state.messages.filter(({channel_id}) => channel_id === this.state.selected_channel_id);
}
render() {
return (
<div className="App">
<ChannelList
channels={this.state.channels}
selectedChannelId={this.state.selected_channel_id}
onSelect={this.onChannelSelect}
/>
<MessagePane messages={this.filteredMessages()} onSendMessage={this.onSendMessage} />
</div>
);
}
}
export default App;
i do not know how to make constant in .env file for firebase. please help me if anyone know about this and how to connect and access firebase database for real time reload. i have also change the rules but it does not work for me.

blueprintjs Table with redux

I have troubles with a blueprintjs table with redux. Here I am trying to maintain the row selection thru redux. I am fairly new to react/redux so maybe I am missing something?
I tried the same component without redux (i.e. the selection modifies the state of the component) and it works fine, but I would prefer to use redux.
Here is a minimal example (jsx) with redux:
import React from "react"
import {Cell, Column, Table, SelectionModes, Regions, RegionLayer} from "#blueprintjs/table"
import ReactDOM from 'react-dom';
import {Provider, connect} from 'react-redux'
import {applyMiddleware, createStore} from 'redux';
import createLogger from 'redux-logger';
export class TableTestRedux extends React.Component {
constructor(props) {
super(props)
this.state={}
}
componentDidUpdate() {
console.log("TableTestRedux:componentDidUpdate props:", this.props)
}
onSelection(e) {
console.log("TableTestRedux:onSelection e:", e)
if (e.length > 0) {
var selectedRow = e[0].rows[0]
this.props.onSelectedEdgeOverrideIdx(selectedRow)
}
}
getSelectedRegion() {
var region = this.props.selectedEdgeOverrideIdx != null ? [Regions.row(this.props.selectedEdgeOverrideIdx)] : []
console.log('TableTestRedux:getSelectedRegion returns ', region);
return region
}
render() {
console.log('TableTestRedux:render props:', this.props);
const renderCell = (rowIndex) => <Cell>{this.props.edgeOverrides[rowIndex]}</Cell>;
return (
<div>
<Table ref="table" numRows={this.props.edgeOverrides.length} onSelection={(e) => this.onSelection(e)}
allowMultipleSelection={false}
selectionModes={SelectionModes.ROWS_ONLY}
selectedRegions={this.getSelectedRegion()}>
<Column name="Description" renderCell={renderCell}/>
</Table>
</div>
)
}
}
// --- redux container/smart component
const mapStateToProps = (state) => {
console.log("TableTestRedux:mapStateToProps ", state);
return {
edgeOverrides: state.edgeOverrides ? state.edgeOverrides : [],
selectedEdgeOverrideIdx: state.selectedEdgeOverrideIdx
}
}
const mapDispatchToProps = (dispatch) => {
console.log("TableTestRedux:mapDispatchToProps ");
return {
onSelectedEdgeOverrideIdx: (selectedEdgeOverrideIdx) => {
dispatch(OverrideEntryActions.selectEdgeOverrideIdx(selectedEdgeOverrideIdx))
}
}
}
export const TableTestReduxCC = connect(mapStateToProps, mapDispatchToProps)(TableTestRedux)
// --- redux action and reducer
export class OverrideEntryActions {
static selectEdgeOverrideIdx(selectedEdgeOverrideIdx) {
return {
type: 'SELECT_EDGE_OVERRIDE_IDX',
selectedEdgeOverrideIdx: selectedEdgeOverrideIdx
}
}
}
const initialOverrideEntryState = {
selectedEdgeOverrideIdx: null,
edgeOverrides: ["bla", "blabla", "more blabla"]
}
export const overrideEntryReducer = (state = initialOverrideEntryState, action) => {
console.log("overrideEntryReducer: action:", action, " state:", state)
switch (action.type) {
case 'SELECT_EDGE_OVERRIDE_IDX':
return {selectedEdgeOverrideIdx: action.selectedEdgeOverrideIdx}
default:
return state
}
}
// --- launch
var store = createStore(overrideEntryReducer, applyMiddleware(createLogger()))
ReactDOM.render((
<Provider store={store}>
<TableTestReduxCC/>
</Provider>
), document.getElementById('app'))
When I click on a row header, TableTestRedux.render() is called and this causes all the table cells to be blank. The log shows:
Warning: NaN is an invalid value for the height css style property. Check the render method of RegionLayer.
The problem was on my side in overrideEntryReducer, I was erasing the edgeOverrides property of the state. Here is the fix:
case 'SELECT_EDGE_OVERRIDE_IDX':
return {...state,selectedEdgeOverrideIdx: action.selectedEdgeOverrideIdx}

Resources