skip to Main Content

I’m learning navigation in React Native, and can’t figure out way how to conditionally use one Back Button.
Wanted behaviour would be like this – if user navigate between screens, he can press Go Back button, but if one of the screens contains WebView and he already visited few pages there, user would go back through this webview pages first, then Screens. Hope that makes sense.
Something like that: Screen A <- Screen B <- Screen C with WebView <- Webview Page 1 <- Webview Page 2

screenshot

AppScreen

import React from 'react';
import {Pressable, StyleSheet, Text} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';

import HomeScreen from './src/screens/HomeScreen';
import WebViewScreen from './src/screens/WebViewScreen';
import ParamsScreen from './src/screens/ParamsScreen';

const Stack = createNativeStackNavigator();

function App(): React.JSX.Element {
  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Home"
        screenOptions={
          ({navigation}) => {}
        }>
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={{title: 'Customized Header Home'}}
        />
        <Stack.Screen
          name="WebView Screen"
          component={WebViewScreen}
          options={({navigation, route}) => ({
            title: 'Webview',
            headerLeft: () => (
              <Pressable onPress={() => navigation.goBack()}>
                <Text style={{color: 'blue'}}>Go Back within Screens</Text>
              </Pressable>
            ),
          })}
        />
        <Stack.Screen name="Params Screen" component={ParamsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

const styles = StyleSheet.create({});

export default App;

WebViewScreen

import {StyleSheet, Text, View, ActivityIndicator, Button} from 'react-native';
import React, {useRef, useState} from 'react';
import {WebView} from 'react-native-webview';

const WebViewScreen = ({navigation, route}) => {
  const webViewRef = useRef<WebView>(null);

  return (
    <>
      <View style={{flex: 0, alignItems: 'center', justifyContent: 'center'}}>
        <Text>WebView Screen</Text>
        <Button
          title="Go Back within WebView"
          onPress={() => webViewRef.current?.goBack()}
        />
      </View>
      <WebView
        source={{uri: 'https://google.com'}}
        startInLoadingState
        ref={webViewRef}
        renderLoading={() => (
          <View style={{flex: 1, alignItems: 'center'}}>
            <ActivityIndicator size="large" />
          </View>
        )}
        allowsBackForwardNavigationGestures
        onNavigationStateChange={navState => {}}
      />
    </>
  );
};

export default WebViewScreen;

const styles = StyleSheet.create({});

At the moment I’ve managed to have two separate working buttons, but it would be much better to have one back button in the header.
I was playing with Screen options and onNavigationStateChange params, and but kind of got lost there.

I guess I would need some kind of function like this:

  const goBack = () => {
if (webViewRef.current) {
  webViewRef.current.goBack();
} else {
  navigation.goBack();
}

};

but not sure how to integrate it with Stack.Navigator

2

Answers


  1. You can achieve the desired behavior by using a combination of headerLeft and onNavigationStateChange in your WebViewScreen. Here’s a modified version of your WebViewScreen that should help you implement the combined back functionality:

    import { StyleSheet, Text, View, ActivityIndicator, Button } from 'react-native';
    import React, { useRef } from 'react';
    import { WebView } from 'react-native-webview';
    
    const WebViewScreen = ({ navigation, route }) => {
      const webViewRef = useRef();
    
      const handleNavigationStateChange = (navState) => {
        // Check if WebView can go back
        const canGoBack = navState.canGoBack;
    
        // Update the header left button based on WebView navigation
        navigation.setOptions({
          headerLeft: () => (
            <View style={{ marginLeft: 10 }}>
              <Button
                onPress={() => {
                  if (canGoBack) {
                    // If WebView can go back, go back in WebView
                    webViewRef.current.goBack();
                  } else {
                    // If WebView cannot go back, go back in the navigation stack
                    navigation.goBack();
                  }
                }}
                title="Go Back"
                color="blue"
              />
            </View>
          ),
        });
      };
    
      return (
        <>
          <View style={{ flex: 0, alignItems: 'center', justifyContent: 'center' }}>
            <Text>WebView Screen</Text>
          </View>
          <WebView
            source={{ uri: 'https://google.com' }}
            startInLoadingState
            ref={webViewRef}
            renderLoading={() => (
              <View style={{ flex: 1, alignItems: 'center' }}>
                <ActivityIndicator size="large" />
              </View>
            )}
            allowsBackForwardNavigationGestures
            onNavigationStateChange={handleNavigationStateChange}
          />
        </>
      );
    };
    
    export default WebViewScreen;
    
    const styles = StyleSheet.create({});
    

    In this code, the handleNavigationStateChange function checks if the WebView can go back (canGoBack). It then updates the headerLeft button accordingly. If the WebView can go back, the button will trigger webViewRef.current.goBack(), otherwise, it will trigger navigation.goBack() to go back in the navigation stack.

    This way, you have a single "Go Back" button in the header that handles both WebView and screen navigation.

    Login or Signup to reply.
  2. I have never worked with WebView, I normally use React Native for Android/iOS dev, still, I have an idea that might work.

    Just do this ( hope you know how to use React Context, if not comment it, I will update the answer ) :

    1. Make a useState array in App() const [history, setHistory] = useState([])
    2. Wrap your app with Context and put history and setHistory in value attribute.
    3. Now, every time you navigate to a new page, just store the Page’s name in array with history.push('WebPage name'), this way you can know if there are pages in background and which and how many.
    4. Than everytime you use go back button, you can check
      history.length? to see if array has any element, if yes you can do go back to last webpage by webViewRef.current?.goBack(), else you can do navigation.goBack() to navigate to last screen.

    Overall it will look like this :
    // go Back button

    <Button
        title="Go Back within WebView"
        onPress={() => history.length? // short hand if-else 
            webViewRef.current?.goBack() : // if yes than go back to last webpage
            navigation.goBack() // if no, go back to screen
        }
            />
    

    Using Context is really easy, comment if you don’t get anything, I will update my answer. Also upvote if this helps, just need a few more reputations 🙂

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search