How to use react-native-background-timer as setTimeout equivalent in React Native (Expo)?
So you would like to use setTimeout in the background of a React Native app, but it doesn’t run in the background. Don’t fret this can be achieved using React Native Background Timer, and the best part is it also works in Expo (with a custom development client)
The Problem
When a React Native app goes into the background, the JavaScript event loop, which powers functions like setTimeout and setInterval, is paused. This means that timers scheduled using setTimeout or setInterval will be temporarily halted and will not execute while the app remains in the background.
When the app returns to the foreground, the event loop resumes, and any pending timers will continue executing as scheduled. However, it’s important to note that if a timer’s delay expires while the app is in the background, the timer callback will not be triggered until the app returns to the foreground.
Using Expo
To use react-native-background-timer in expo you’ll need to add it as a plugin and make a custom development client.
Add this to your projects app.json
"plugins": [
…,
[
"./plugins/backgroundTimer/app.plugin.js",
"backgroundTimer"
],
],
Create ./plugins/backgroundTimer/app.plugin.js with the following:
module.exports = require('./withBackgroundTimer.js');
Create ./plugins/backgroundTimer/withBackgroundTimer.js with the following:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const config_plugins_1 = require("@expo/config-plugins");
const pkg = require('../../node_modules/react-native-background-timer/package.json');
const withVoice = (config, props = {}) => {
const _props = props ? props : {};
return config;
};
exports.default = config_plugins_1.createRunOncePlugin(withVoice, pkg.name, pkg.version);
Then build your custom development client https://docs.expo.dev/development/getting-started/
For installation without Expo please follow the instruction on the packages GitHub page.
Using their cross platform approach
import BackgroundTimer from 'react-native-background-timer';
BackgroundTimer.runBackgroundTimer(() => {
//code that will be called every 3 seconds
},
3000);
//rest of code will be performing for iOS on background too
BackgroundTimer.stopBackgroundTimer(); //after this call all code on background stop run.
This works if you want to emulate JavaScript setIntervalS, but what about setTimeout?
The following function creates a reusable example
const autoTimeout = React.useRef(null);
// React Ref within your component
const autoTImeoutSetter = ({ thenRun, duration }) => {
if(autoTimeout.current){
BackgroundTimer.stopBackgroundTimer(autoTimeout.current)
autoTimeout.current = null
}
autoTimeout.current = new Promise((resolve,reject)=>{
BackgroundTimer.runBackgroundTimer(() => {
//code that will be called every 3 seconds
thenRun()
resolve(true)
}, duration);
})
// clean up after first run
autoTimeout.current.then(()=>{
BackgroundTimer.stopBackgroundTimer(autoTimeout.current)
autoTimeout.current = null
})
};
You now have a functioning setTimeout that will run even while the app is in the background that is presuming you have enabled a background services in your app.
You can now call your background setTimeout function with
autoTImeoutSetter({ thenRun:()=>console.log('backgroundTimer'), duration:5000 })
Whichever function is passed as the thenRun argument will be called after the duration has elapsed.