Expo TaskManager - How do I pass a changed state to an outside function?

Viewed 819

I am working on an app with react-native and expo and I am having trouble getting a state change to pass into my task manager and I am wondering where I am going wrong. I am unsure if maybe expo task manager can't really accept a state change? Basically when I start my task, I check is a state variable that is passed is true or false and then I want that change the state. It works once but then state in task manager seems to stay the same meaning it keeps trying to check me in because it still sees the submitted variable as false:

***********APP.JS STATUS: true
"****LOCATION PINGING... submitted IS NOW:","false"

task.js

import * as TaskManager from 'expo-task-manager';
const TASK_FETCH_LOCATION_TEST = 'background-location-task';


export const configureBgTasks = ({ submitted, autoCheckin, autoCheckout }) => {
    
    TaskManager.defineTask(TASK_FETCH_LOCATION_TEST, ({ data, error }) => {

        if (error) {
            // Error occurred - check `error.message` for more details.
            return;
        }
        if (data) {
            //get location data from background
            const { locations } = data;
            console.log('****LOCATION PINGING... submitted IS NOW:', submitted);
            if (submitted === false) {
                autoCheckin();
                console.log('****CHECKING YOU IN...');
            } else if(submitted === true) {
                autoCheckout();
                console.log('*****CHECKING YOU OUT...')
            }
        }
    })
}

App.js

import React, { Component } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, Button, Platform, Alert } from 'react-native';
import * as Location from "expo-location";
import { configureBgTasks } from './task';
import * as TaskManager from 'expo-task-manager';
const TASK_FETCH_LOCATION_TEST = 'background-location-task';

class App extends Component {

  state = {
    submitted: false
  }


  async componentDidMount() {
    const { status } = await Location.requestPermissionsAsync();
    if (status === 'granted') {
      console.log('location permissions are granted...')
    }
  }
  
    stopBackgroundUpdate = async () => {
      Alert.alert('TRACKING IS STOPPED');
      //Location.stopLocationUpdatesAsync(TASK_FETCH_LOCATION_TEST)
  
      //UNREGISTER TASK
      //const TASK_FETCH_LOCATION_TEST = 'background-location-task_global';
      TaskManager.unregisterTaskAsync(TASK_FETCH_LOCATION_TEST);
    }


    //REFERENCES TO STATE
    autoTrackingCheckin = () => {
      console.log('^^firing checkin')
      this.setState({ submitted: true });
    }

    autoTrackingCheckout = () => {
      console.log('^^firing checkout')
      this.setState({ submitted: false });
    }
  

    executeBackground = async () => {

      //START LOCATION TRACKING
      const startBackgroundUpdate = async () => {
        Alert.alert('TRACKING IS STARTED');
    
        if(Platform.OS==='ios') {
    
          await Location.startLocationUpdatesAsync(TASK_FETCH_LOCATION_TEST, {
            accuracy: Location.Accuracy.BestForNavigation,
            //timeInterval: 1000,
            distanceInterval: 2, // minimum change (in meters) betweens updates
            //deferredUpdatesInterval: 1000, // minimum interval (in milliseconds) between updates
            // foregroundService is how you get the task to be updated as often as would be if the app was open
            foregroundService: {
              notificationTitle: 'Using your location for TESTING',
              notificationBody: 'To turn off, go back to the app and toggle tracking.',
            },
            pausesUpdatesAutomatically: false,
          });
    
        } else {
    
          await Location.startLocationUpdatesAsync(TASK_FETCH_LOCATION_TEST, {
            accuracy: Location.Accuracy.BestForNavigation,
            timeInterval: 1000,
            //distanceInterval: 1, // minimum change (in meters) betweens updates
            //deferredUpdatesInterval: 1000, // minimum interval (in milliseconds) between updates
            // foregroundService is how you get the task to be updated as often as would be if the app was open
            foregroundService: {
              notificationTitle: 'Using your location for TESTING',
              notificationBody: 'To turn off, go back to the app and toggle tracking.',
            },
            pausesUpdatesAutomatically: false,
          });
  
        }
      }



       //WHERE THE MAGIC IS SUPPOSED TO HAPPEN
        try {

          //REFERENCES FOR VARIABLES AND FUNCTIONS
          const submitted = this.state.submitted
          const autoCheckin = this.autoTrackingCheckin
          const autoCheckout = this.autoTrackingCheckout
          
          console.log('THE VARIABLE BEING PASSED...',submitted)
          configureBgTasks({ submitted, autoCheckin, autoCheckout })
          startBackgroundUpdate();
        }
        catch (error) {
          console.log(error)
        }


    }


  


  render() {

    console.log('***********APP.JS STATUS:', this.state.submitted);

    return (
      <View style={styles.container}>
      
      <Button
          onPress={this.executeBackground}
          title="START TRACKING"
        />
        
        <Button
          onPress={this.stopBackgroundUpdate}
          title="STOP TRACKING"
        />
      <StatusBar style="auto" />
      </View>
    );
  }
}

export default App;



const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'space-evenly',
  },
});
1 Answers

According to the expo's documentation, all tasks need to be defined out of the react-native code, in the global scope:

It must be called in the global scope of your JavaScript bundle. In particular, it cannot be called in any of React lifecycle methods like componentDidMount. This limitation is due to the fact that when the application is launched in the background, we need to spin up your JavaScript app, run your task and then shut down — no views are mounted in this scenario.

Therefore, I believe the best solution for you is to use Redux/Context to achieve a state behavior (as you can read here)

Related