I'm trying to find a way to execute a function upon termination of the app, what function can I use to do this in react native?
Basically, whenever the user closes the app (completely terminate).
This might help:
AppState.addEventListener('change', state => {
if (state === 'active') {
// do this
} else if (state === 'background') {
// do that
} else if (state === 'inactive') {
// do that other thing
}
});
You can use the AppState API provided by react-native in your root component where you register/mount your app.
There you can add an onChange eventlistener to AppState which executes your custom function whenever AppState.currentState.match(/inactive|background/) returns true. Your custom function however should be isolated from your other component states.
Related
I have a react native application using redux for state management.
On every API request, I have actions dispatched which is handled by reducer. This is then forwarded to saga and back to reducer.
I am using snackbar to show any errors which might have happened.
e.g. In the login flow, one of the operations is to fetch the OTP for username.
The action types are:
GET_OTP_FOR_USER_REQUEST
GET_OTP_FOR_USER_SUCCESS
GET_OTP_FOR_USER_FAILURE
The initial state for the reducer (LoginReducer) is
{
hasError: false,
error : {
display_message : "",
details : null
}
otherData : []
}
Now in case of GET_OTP_FOR_USER_FAILURE, the reducer will be updated to
{
hasError: true,
error : {
display_message : "Invalid Username",
details : null
}
otherData : []
}
In my view component, I conditionally render the snackbar based on the hasError flag
{this.props.hasError ? Snackbar.show({
title: this.props.error.display_message,
duration: Snackbar.LENGTH_LONG
}) : null}
If I don't reset the hasError, then the snackbar keeps coming for every setState call (which happens on textinputchange).
The current approach I am using is, I have an action to RESET_ERROR.
I call this action once the setState is called on the textbox on the component (in this case the username)
onUserNameTextChanged = (text) => {
this.props.resetError(); //Reset the error
this.setState({ //something });
}
The issue with this is that this.props.resetError(); will get called on every character update.
Just to ensure that I don't call render multiple time, I am using shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState){
if(this.props.hasError && (this.props.hasError === nextProps.hasError))
return false;
else
return true;
}
I am not sure if there is simpler or cleaner approach to handle these scenarios. Any directions will be helpful.
You can just conditionally call it if there's an error, that way you're not always invoking it:
onUserNameTextChanged = (text) => {
if (this.props.hasError) {
this.props.resetError(); //Reset the error
}
this.setState({ //something });
}
You can also tie it into the success action if you want that error to show until the API returns successfully.
when I press physical return button on my phone the log have no output until back root route
componentWillMount(){
BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid)
}
_onBackAndroid = () => {
console.log(this.props.navigation.state.routeName)
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
return false;
}
this.lastBackPressed = Date.now();
toastShort('Press Again Exit App');
return true;
};
componentWillUnmount(){
BackHandler.removeEventListener('hardwareBackPress', this._onBackAndroid)
}
By design, react-navigation takes full control of the back button unless you handle the state yourself (by using something like Redux).
You can take a look at this issue, which is referencing a similar situation.
Does anyone know a way in which I can detect when the user has opened their phone? To my understanding android.intent.USER_PRESENT is broadcast when the device is unlocked (e.g. correct password entered), however, I do not know how to detect the broadcast in with React native. Does anyone have a solution?
Look into AppState API in FB(Face Book)
It has 3 states on FB and 5 total. I only see 3 in FB but the other 2 may or may not be suppprted.
Active - The app is running in the foreground
background - The app is running in the background. The user is either in another app or on the home screen.
inactive - This is a state that occurs when transitioning between foreground & background, and during periods of inactivity such as entering the Multitasking view or in the event of an incoming call
Check out apples Docs for more on these.
You're going to have to test what state is hit when you the phone is in the lockscreen. I can't tell you what state to use because I have never tested the api out.
as you can see from the code below the test is done in a conditional statement
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active')
I'm taking facebooks example here but attach the change event listiner in componentDidMount and remove in ComponentWillUnmount and the code will run accourding to the state.
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>
);
}
}
I want to dispatch a action when drawer close.But I don't know I need write what function, can you help me.
You need to custom navigation actions to know when the DrawerClose event fired.Here is a simple example:
const MyAppDrawerNavigator = DrawerNavigator({
//...
});
const defaultGetStateForAction = MyAppDrawerNavigator.router.getStateForAction;
MyAppDrawerNavigator.router.getStateForAction = (action, state) => {
if (state && action.type === 'Navigation/NAVIGATE' && action.routeName === 'DrawerClose') {
console.log('DrawerClose');
//dispatch whatever action you want
}
return defaultGetStateForAction(action, state);
};
To known more about how to custom routers, see here.
I'm using React Native's Navigator. Is there anyway to refresh the component so when I pop back to it, it'll make a new API call and grab the updated data to display in the component. I found a few similar questions, but no good answer...
Adding Api Call in callBack using a subscription. sovles the issue
componentDidMount() {
this.props.fetchData();
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
() => {
this.props.fetchData();
}
);
}
componentWillUnmount() {
this.willFocusSubscription.remove();
}
You can send a callback function to nextscene from previous one as a prop.
this.props.navigator.push({
name: *nextscene*,
passProps: {
text: response,
callBack: this.callback
});
async callback(){
await ....//make new api request grab the udpated data
}
Then in your nextscene you call callback method and then pop. You can also send parameters
this.props.callBack()
this.props.navigator.pop()
When pop () to refresh before a page is not a good idea
You can try DeviceEventEmitter object
previous page DeviceEventEmitter.addListener('xxx', callback) in componentDidMount
current page DeviceEventEmitter.emit('xxx', anythingInCallback...) before pop()
ps:previous pageDeviceEventEmitter.removeAllListeners('xxx') in componentWillUnmount
I doubt you're still looking for an answer to this, but holy crap has this kept me up tonight. I'm very new to React Native, but I finally had some success.
The React Navigation API docs have a section for adding event listeners! Check it out! I shared some of my own code below too.
This is an example event handler in a Component that is the top screen of the StackNavigator stack. It grabs the current state and saves to the backend using an API call. After completion, StackNavigator's pop is called.
handleSubmit = () => {
const { value, otherValue } = this.state
addThingToDatabase({ value, otherValue })
.then(() => this.props.navigation.pop())
}
Now over to the other Component which is the screen "underneath" in the StackNavigator stack. This is screen being shown after the "pop". Here's what I used to have in ComponentDidMount.
componentDidMount() {
const { index } = this.props.navigation.state.params
getAllThingsFromDatabase({ index })
.then(({ arrayOfThings }) => this.setState({
index,
arrayOfThings
}))
}
But the Component wouldn't update with the new thing, until addListener! Now I have pretty much the same code except it's in the constructor. I figured I only need to run it one time, and I need to store it too.
constructor(props, context) {
super(props, context)
this.state = {
index: null,
arrayOfThings: []
}
this.willFocusSubscription = this.props.navigation.addListener(
'willFocus',
(payload) => {
const { index } = payload.state.params
getAllThingsFromDatabase({ index })
.then(({ arrayOfThings }) => this.setState({
index,
arrayOfThings
}))
}
)
}
Note that the docs also mention unsubscribing the event listener using the .remove() function. I put that in ComponentWillUnmount().
componentWillUnmount() {
this.willFocusSubscription.remove()
}
There are four different events to subscribe to. I went with willFocus thinking it'll update before the screen is seen.
You should save the state of the page and emit an action in componentDidMount since it is invoked immediately after a component is mounted.
References:
https://facebook.github.io/react/docs/react-component.html
https://github.com/ReactTraining/react-router
ADDED
Since your component has been already mounted you should listen ComonentWillReceiveProps instead.
The simple way is to use react native navigation resetTo function. It will replace the top item and pop to it.
If we do like this componentWillReceiveProps will call. So we can provide the API calls in that function and make it simple.
for more details https://facebook.github.io/react-native/docs/navigatorios.html#resetto