Search code examples
react-nativeasync-awaitreact-navigationes6-promisetouchableopacity

TouchableOpacity enabled/disabled using an async promise


First of all, the touchableOpacity button is in a react navigation header. I create a stackNavigator like so:

import { DownloadedToday } from './src/components/Functions'
const navigator = createStackNavigator({
  Home: {
    screen: HomeScreen,
    params: {
      button: false,
    },
    navigationOptions: ({ navigation }) => ({
      title: 'Home',
      headerTitle: '',
      headerRight: () => (
        <TouchableOpacity style={styles.headerRight}
          disabled = {***boolean***}
        >
          <AntDesign
            name = 'download'
            size = {23}
            color = 'grey'
          />
        </TouchableOpacity>
      )
    })
  })

I have an async function in another file that I use to return a boolean:

export const DownloadedToday = async() => {
  //get the current date
  let currentDate = new Date().toDateString()
  //get the last download date
  let lastUpdate = await RetrieveData('downloadDate')

  if(lastUpdate === currentDate){
    return true
  }else{
    return false
  }

}

No problem at all returning the boolean from the function. I think the issue is that I am working with promises so I cant just assign a variable and plug it in - the async functions always return the promise. Im new to these issues so I am having the HARDEST time trying to figure out how to use the async function and plug it in to the TouchableOpacity disabled prop. Any help is SUPER appreciated


Solution

  • As I wrote in the comments, the critical situation here is that in the navigationOptions you can just rely on navigation prop in order to re-render the header. I first think about React Context API but you cannot access the context there.

    1. Basically, your DownloadedToday function needs to be inside a React screen which is part of the navigation system in order to change the navigation prop: a beatiful idea could be create a custom hook to fetch the value and use it in the current screen (I can show you this also if you ask me).

    2. include your custom hook in your screen and subscribe to its changes:

    const { lastUpdate } = useYourCustomHook()
    useEffect(() => {
      //
    }, [lastUpdate])
    
    1. now we can play with navigation params: the idea is to edit a specific param that will be responsible to disable the button, let's call it disableHeaderButton; assuming that this will defaults to false (button enabled by default), the useEffect will become:
    useEffect(() => {
      // if lastUpdate is true, we will trigger a navigation action which will disable the button
      if (lastUpdate) navigation.setParams({ disableHeaderButton: true })
    }, [lastUpdate])
    
    1. Edit your stackNavigator in order to set a default value for disableHeaderButton param!

    2. Edit the navigationOptions into your stack like this:

        navigationOptions: ({ navigation }) => ({
          title: 'Home',
          headerTitle: '',
          headerRight: () => (
            <TouchableOpacity
              style={styles.headerRight}
              disabled={navigation.state?.routes[navigation.state.index].params?.disableHeaderButton}
            >
              <AntDesign
                name = 'download'
                size = {23}
                color = 'grey'
              />
            </TouchableOpacity>
          )
        })
    

    What is navigation.state?.routes[navigation.state.index].params?.disableHeaderButton ?

    With this expression, you tell react-navigation to look at the params of the current active screen (routes is the array of the routes indexed by their current state.index), so if the param is edited, your navigation header now knows that it's time to re-render!