skip to Main Content

I’m trying to display a confirmation modal on back-press using BackHandler in a React Native application. I have a local state of type string (postText) that holds some text. If the length of postText is greater than 0, I want to show a confirmation modal and prevent the default back action.

However, the condition (postText?.length > 0) is not working as expected. The back button always performs the default action, even when the condition is true. Here’s my code:

import React, { useState, useEffect } from 'react';
import { BackHandler } from 'react-native';

const App = () => {
  const [postText, setPostText] = useState<string>('');

  useEffect(() => {
    const onBack = () => {
      if (postText?.length > 0) {
        // Show confirmation modal
        console.log('Showing confirmation modal');
        return true; // Prevent default back action
      }
      return false; // Allow default back action
    };

    const subscription = BackHandler.addEventListener(
      'hardwareBackPress',
      onBack
    );

    return () => {
      subscription.remove();
    };
  }, [postText]); // Dependency array includes postText

  return null; // Simplified for brevity
};

export default App;

2

Answers


  1. The issue here is that BackHandler.addEventListener returns a subscription object only on iOS. On Android, BackHandler uses a callback-based API where you directly handle the back press, and subscription.remove is not available.

    Instead, you should use the callback format directly.

    useEffect(() => {
        const onBack = () => {
          if (postText?.length > 0) {
            // Show confirmation modal
            console.log('Showing confirmation modal');
            return true; // Prevent default back action
          }
          return false; // Allow default back action
        };
    
        BackHandler.addEventListener(
          'hardwareBackPress',
          onBack
        );
    
        return () => {
          BackHandler.removeEventListener('hardwareBackPress', onBack);
        };
      }, [postText]);
    
    Login or Signup to reply.
  2. Possible that the useEffect persists the initial value of postText and doesn’t update on changes if the dependency array is not properly defined or managed. This happens because the closure of the useEffect function captures the state of postText at the time of the initial render and doesn’t reflect subsequent updates unless explicitly instructed through dependencies. Use a stable ref for postText to prevent dependency re-triggering issues.

    import React, { useState, useEffect, useRef } from 'react';
    import { BackHandler, Alert } from 'react-native';
    
    const App = () => {
      const [postText, setPostText] = useState<string>('');
      const postTextRef = useRef(postText);
    
      useEffect(() => {
        postTextRef.current = postText; // Keep the ref updated
      }, [postText]);
    
      useEffect(() => {
        const onBack = () => {
          if (postTextRef.current.length > 0) {
            return true; // Prevent default back action
          }
          return false; // Allow default back action
        };
    
        BackHandler.addEventListener('hardwareBackPress', onBack);
    
        return () => {
          BackHandler.removeEventListener('hardwareBackPress', onBack);
        };
      }, []);
    
      return null; // Simplified for brevity
    };
    
    export default App;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search