Working with App State and Event Listeners in React Native

How to manage app state with event listeners and React hooks

How to Use App State

// get current app state from `AppState`import React, { useState } from 'react'
import { AppState } from 'react-native'
const App = (props) => {

const [appState, setAppState] = useState(AppState.currentState);
...
}
// `AppState` event listeners within `useEffect`const handleAppStateChange = (state: any) => {
console.log(state);
}
useEffect(() => {
AppState.addEventListener('change', handleAppStateChange);
return (() => {
AppState.removeEventListener('change', handleAppStateChange);
})
}, []);
// listening to `AppState` changesconst [appState, setAppState] = useState(AppState.currentState);const handleAppStateChange = (state: any) => {
setAppState(state);
}
useEffect(() => {
AppState.addEventListener('change', handleAppStateChange);
return (() => {
AppState.removeEventListener('change', handleAppStateChange);
})
}, []);
useEffect(() => {
console.log(appState);
});

When AppState changes happen

Demonstrating AppState changes as the app changes from foreground, background and app-switcher

Notice how some apps blur their screens in app switcher?

// render `inactive` screen via top-level `AppStateManager` componentexport const AppStateManager = (props: any) => {  const [appState, setAppState] = useState(AppState.currentState);  const handleAppStateChange = (state: any) => {
setAppState(state);
}
useEffect(() => {
AppState.addEventListener('change', handleAppStateChange);
return (() => {
AppState.removeEventListener('change', handleAppStateChange);
})
}, []);
return (
{appState === 'inactive'
? <View><Text>Inactive Screen!</Text><View>
: <>{props.children}</>
}
)

}

Using Refs with Event Listeners to Access True State Values

const [counter, setCounter] = useState(0);// update state every 2 secondssetInterval(() => {
setCounter(counter + 1);
}, 2000);
// console.log `counter` within event listener every 2 secondsuseEffect(() => {
this.focusListener = props.navigation.addListener('didFocus', async () => {
setInterval(() => {
// this will always be 0
console.log(counter);
}, 2000);
});

return (() => {
this.focusListener.remove();
})
}, []);

What about getting current HTML / JSX element state within event listeners?

const scrollview = React.createRef();
const scrollviewRef = React.useRef(scrollview);
// INCORRECT: attempting to assign `scrollview` as ref and use within event listenerconst scrollview = React.createRef();useEffect(() => {
this.focusListener = props.navigation.addListener('didFocus', async () => {
// this will be `null`
console.log(scrollview.current);
});
return (() => {
this.focusListener.remove();
})
}, []);

return (
<ScrollView
ref={scrollview}
...
/>
);
// CORRECT: accessing `ScrollView` ref within event listenerconst scrollview = React.createRef();
const scrollviewRef: any = React.useRef(scrollview);
useEffect(() => {
this.focusListener = props.navigation.addListener('didFocus', async () => {
// this will now successfully reference <ScrollView />
console.log(scrollviewRef.current);
});
return (() => {
this.focusListener.remove();
})
}, []);
return (
<ScrollView
ref={scrollviewRef}
...
/>
);

How does this apply to AppState?

Working with a Timer and AppState

Defining the Timer class

yarn add moment

Wrapping Timer in a Context Provider

Controlling a Timer with AppState

In Summary

Further Reading

Programmer and Author. Director @ JKRBInvestments.com. Creator of LearnChineseGrammar.com for iOS.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store