skip to Main Content
import { useIsFocused } from "@react-navigation/native";
import { useRouter } from "expo-router";
import React, { useEffect, useState } from "react";
import { Alert, Modal, StyleSheet, Text, Pressable, View } from "react-native";
import { SafeAreaView, SafeAreaProvider } from "react-native-safe-area-context";

const TestingScreen = () => {
  const [modalVisible, setModalVisible] = useState(false);
  const route = useRouter();
  const isFocused = useIsFocused();

  useEffect(() => {
    if (isFocused && modalVisible) {
      setModalVisible(true);
    }
  }, [isFocused]);

  return (
    <SafeAreaProvider>
      <SafeAreaView style={styles.centeredView}>
        <Modal
          animationType="slide"
          transparent={true}
          visible={modalVisible}
          onRequestClose={() => {
            Alert.alert("Modal has been closed.");
            setModalVisible(!modalVisible);
          }}
        >
          <View style={styles.centeredView}>
            <View style={styles.modalView}>
              <Text onPress={() => route.navigate("/user/user-profile")}>
                go to user-profile
              </Text>
              <Pressable
                style={[styles.button, styles.buttonClose]}
                onPress={() => setModalVisible(!modalVisible)}
              >
                <Text style={styles.textStyle}>Hide Modal</Text>
              </Pressable>
            </View>
          </View>
        </Modal>
        <Pressable
          style={[styles.button, styles.buttonOpen]}
          onPress={() => setModalVisible(true)}
        >
          <Text style={styles.textStyle}>Show Modal</Text>
        </Pressable>
      </SafeAreaView>
    </SafeAreaProvider>
  );
};

//screen to navigate and return

import { useRouter } from "expo-router";
import { View, Text,} from "react-native";

export default function UserProfile() {
  const route = useRouter();
  return (
    <View style={{ flex: 1, padding: 40 }}>
      <Text onPress={()=>route.back()}>testing screen</Text>
    </View>
  );
}

I have tried using useFocused or useFocusEffect for when the state of the modal changes but what happens is that when I return from the screen, the modal simply disappears and does not allow me to open it again, what can I do?

2

Answers


  1. Chosen as BEST ANSWER

    My solution:

    Call the modal inside the main modal

    const TestingScreen = () => {
      const [showModal, setShowModal] = useState(false);
      const [showProfile, setShowProfile] = useState(false);
    
      const toggleModal = () => {
        setShowModal(!showModal);
        setShowProfile(false);
      };
    
      const toggleProfile = () => {
        setShowProfile(!showProfile);
      };
    
      return (
        <View style={styles.container}>
          <Pressable style={styles.button} onPress={toggleModal}>
            <Text style={styles.buttonText}>Open Modal</Text>
          </Pressable>
    
          <Modal
            animationType="slide"
            transparent={true}
            visible={showModal}
            onRequestClose={toggleModal}
            statusBarTranslucent
          >
            <View style={styles.modalContainer}>
              <View style={styles.modalContent}>
                <Text style={styles.modalText}>this is a modal</Text>
                <Pressable style={styles.button} onPress={toggleProfile}>
                  <Text style={styles.buttonText}>
                    {showProfile ? "Hide Profile" : "Show Profile"}
                  </Text>
                </Pressable>
    
                <Pressable
                  style={[styles.button, styles.closeButton]}
                  onPress={toggleModal}
                >
                  <Text style={styles.buttonText}>Close Modal</Text>
                </Pressable>
              </View>
            </View>
            <UserProfile showProfile={showProfile} toggleProfile={toggleProfile} /> {/*call the modal here*/}
          </Modal>
        </View>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
      },
      button: {
        backgroundColor: "#6200ee",
        padding: 12,
        borderRadius: 8,
        marginTop: 10,
      },
      buttonText: {
        color: "white",
        fontSize: 16,
        fontWeight: "bold",
      },
      modalContainer: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center",
        backgroundColor: "rgba(0, 0, 0, 0.5)",
      },
      modalContent: {
        width: 300,
        backgroundColor: "white",
        padding: 20,
        borderRadius: 10,
        alignItems: "center",
      },
      modalText: {
        fontSize: 18,
        marginBottom: 15,
      },
      closeButton: {
        marginTop: 20,
        backgroundColor: "#b00020",
      },
    });
    

    Apply animations to make it more attractive

    import { View, Text, StyleSheet, Modal, Pressable, Animated } from "react-native";
    import { useEffect, useRef, useState } from "react";
    
    export default function UserProfile({ showProfile, toggleProfile }: any) {
      const slideAnim = useRef(new Animated.Value(400)).current; 
      const [modalVisible, setModalVisible] = useState(showProfile);
    
      useEffect(() => {
        if (showProfile) {
          setModalVisible(true);
          Animated.spring(slideAnim, {
            toValue: 0,
            useNativeDriver: true,
            tension: 65,
            friction: 11
          }).start();
        } else {
          Animated.spring(slideAnim, {
            toValue: 400,
            useNativeDriver: true,
            tension: 65,
            friction: 11
          }).start(() => {
            setModalVisible(false);
          });
        }
      }, [showProfile]);
    
      const handleClose = () => {
        toggleProfile();
      };
    
    
    return (
        <Modal
          transparent={true}
          visible={modalVisible}
          onRequestClose={handleClose}
          animationType="none"
          statusBarTranslucent
        >
          <View style={styles.container}>
            <Animated.View 
              style={[
                styles.profileOverlay,
                {
                  transform: [{ translateX: slideAnim }]
                }
              ]}
            >
              <Pressable onPress={toggleProfile}>
                <Text>Close Profile</Text>
              </Pressable>
            </Animated.View>
          </View>
        </Modal>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
      },
      profileOverlay: {
        position: 'absolute',
        right: 0,
        top: 0,
        bottom: 0,
        width: '100%',
        backgroundColor: "white",
        justifyContent: "center",
        alignItems: "center",
        elevation: 5,
      },
    });   
    

  2. This is a great question.

    If I understand, you leave the testing screen and then return after navigating to another component.

    In react, when you navigate to a new URL, the component you started in is trashed, therefore, all of the state is lost.

    So when the following action is performed, the state variables you have stored are lost:

    <Text onPress={() => route.navigate("/user/user-profile")}>
                    go to user-profile
                  </Text>

    To fix this issue, you have a couple options:

    1. Using redux to manage modal open state:

    If you already use redux, moving your ‘modalVisible’ state variable to a slice would be very easy. However, this is a heavy-handed approach and is not recommended for this problem.

    2. Rendering the login component conditionally instead of navigating to a different page

    import { useIsFocused } from "@react-navigation/native";
    import { useRouter } from "expo-router";
    import React, { useEffect, useState } from "react";
    import { Alert, Modal, StyleSheet, Text, Pressable, View } from "react-native";
    import { SafeAreaView, SafeAreaProvider } from "react-native-safe-area-context";
    
    const TestingScreen = () => {
      const [modalVisible, setModalVisible] = useState(false);
      const [showProfileScreen, setShowProfileScreen] = useState(false);
      const route = useRouter();
      const isFocused = useIsFocused();
    
      useEffect(() => {
        if (isFocused && modalVisible) {
          setModalVisible(true);
        }
      }, [isFocused]);
    
      return (
      <>
        {!showProfileScreen && <SafeAreaProvider>
          <SafeAreaView style={styles.centeredView}>
            <Modal
              animationType="slide"
              transparent={true}
              visible={modalVisible}
              onRequestClose={() => {
                Alert.alert("Modal has been closed.");
                setModalVisible(!modalVisible);
              }}
            >
              <View style={styles.centeredView}>
                <View style={styles.modalView}>
                  <Text onPress={() => route.navigate("/user/user-profile")}>
                    go to user-profile
                  </Text>
                  <Pressable
                    style={[styles.button, styles.buttonClose]}
                    onPress={() => setModalVisible(!modalVisible)}
                  >
                    <Text style={styles.textStyle}>Hide Modal</Text>
                  </Pressable>
                </View>
              </View>
            </Modal>
            <Pressable
              style={[styles.button, styles.buttonOpen]}
              onPress={() => setModalVisible(true)}
            >
              <Text style={styles.textStyle}>Show Modal</Text>
            </Pressable>
          </SafeAreaView>
        </SafeAreaProvider>}
        {showProfileScreen && <UserProfile />}
        }
        </>
      );
    };

    This solution has the benefit of maintaining the state variables because instead of changing your href and causing a re-render, you simply change what you are showing and stay on the same URL.

    Admittedly, this solution can lead to hard-to-read code, so I would recommend using this sparingly.

    See inline conditionals for more info: Conditional Rendering React Docs

    3. Using contexts

    Contexts are a way to pass data down to all child components without specifying a prop. This approach would work for what you need but would definitely require some refactoring. See the React Docs on contexts for more information.

    Hope this helps!!

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