Skip to content

Usage of Workmanager in React Native

NISHANTH BHAT edited this page Apr 11, 2025 · 1 revision

πŸš€ Introduction

WorkManager allows us to run background tasks reliably, even when the app is not running or the device is restarted. In the context of React Native, this enables powerful background capabilities such as syncing data, scheduling jobs, or processing tasks without needing the app to be actively open.

With the right setup, we can configure WorkManager to trigger and execute custom JavaScript functions in React Native β€” enabling true background sync and logic execution, independent of the app’s foreground state.


βš™οΈ How it Works Under the Hood

At the heart of the background sync logic is the BackgroundSyncWorker class, which extends a custom HeadlessWorker. This worker is triggered by Android's WorkManager and is responsible for setting up and executing JavaScript tasks, even when the app is terminated.

πŸ” 1. getTaskConfig() Method

This method is the core of the worker. It tells React Native what task to run and how to run it.

override fun getTaskConfig(data: Data?): HeadlessJsTaskConfig?
  • It receives a Data object from WorkManager that contains all the necessary task parameters.

  • This data is then parsed into:

    • taskKey: Identifier of the JavaScript task to run.
    • maxRetryAttempts: Number of times to retry the task on failure.
    • retryDelay: Delay (in ms) between retries.
    • taskTimeout: Timeout for the JS task (after which it's considered failed).
    • allowedInForeground: Whether this task is allowed to run while the app is in the foreground.

πŸ” 2. Retry Policy

A retry policy is configured using LinearCountingRetryPolicy, which will retry the JS task a specified number of times with a fixed delay.

val retryPolicy: HeadlessJsTaskRetryPolicy = LinearCountingRetryPolicy(
  maxRetryAttempts,
  retryDelay,
)

🧩 3. Returning the Task Configuration

The HeadlessJsTaskConfig tells React Native which task to execute and includes all the runtime options like timeout, retry behavior, and whether it can run in the foreground.

return HeadlessJsTaskConfig(
  taskKey,
  Arguments.makeNativeMap(data.keyValueMap),
  taskTimeout,
  allowedInForeground,
  retryPolicy
)

πŸ“œ Guidelines for Writing Background JavaScript Tasks in React Native

When running background tasks via WorkManager, JavaScript code is executed in a headless context β€” meaning it runs without the app UI or React component tree. This is powerful but requires following certain rules to avoid runtime errors and ensure reliable execution.


βœ… Do's

  1. Use Pure Functions or Independent Logic
    Write self-contained logic that does not rely on React components, hooks, or app state.

    // tasks/backgroundSync.js
    export default async function backgroundSyncTask(data) {
      const response = await fetch('https://example.com/api/sync');
      const json = await response.json();
      // Save data to storage/database
    }
  2. Use Async/Await or Promises
    The function should return a Promise so the native layer knows when it completes.

  3. Handle Errors Gracefully
    Always wrap logic in try/catch to handle failures and avoid crashes.

  4. Use Lightweight, Non-UI Libraries
    You can use libraries like axios, date-fns, AsyncStorage, etc.
    Avoid anything that relies on the UI or user interaction.

  5. Access Native Modules
    You can use NativeModules and call any native methods registered to the bridge.
    However, ensure that these native modules do not trigger any UI operations or navigation.


❌ Don'ts

  1. Do Not Use React Hooks (useEffect, useState, etc.)
    Hooks depend on the React rendering lifecycle, which is not available in a headless JS context.

  2. Avoid Importing UI Components
    No <View>, <Text>, or custom screens β€” there is no UI to display.

  3. Do Not Use Navigation or Routing
    You cannot trigger react-navigation actions or navigate screens.

  4. Avoid UI-bound State Managers
    Hooks like useSelector or useDispatch won't work.
    If needed, use raw Redux store logic without the React bindings.

  5. Avoid Long-Running or Heavy Tasks
    Android may terminate long or CPU-intensive tasks. Keep your logic quick and efficient.

  6. Avoid UI Timing APIs (setTimeout, setInterval, etc.)
    These may not behave reliably in a background context and can cause your task to stall or crash.
    Use promise-based timing (await new Promise) if absolutely necessary β€” and keep durations short.