skip to Main Content

I am fairly new to React Native and I am struggling to get a child view to update/re-render when it should. The code is pasted below. UserProfile contains a tab bar with Posts, Diet, Workouts, and Gallery. UserProfileGalleryView contains an image grid. On click of an image, a modal view should open displaying the image, but currently it will only show once I click an image, tap posts/diet/workout, then tap to go back to Gallery. Once I tap back to gallery, the modal view shows and the close button does not close it. It is evident that my state is not being managed correctly. I have tried using useEffect and useContext to no avail. Any help would be greatly appreciated.

UserProfile (parent component):

import { Auth } from "aws-amplify";
import React, { useEffect } from "react";
import {
  View,
  SafeAreaView,
  Text,
  StyleSheet,
  Pressable,
  Image,
  TurboModuleRegistry,
} from "react-native";
import UserProfileWorkoutsView from "./UserProfileWorkoutsView";
import UserProfileDietView from "./UserProfileDietView";
import UserProfileGalleryView from "./UserProfileGalleryView";
import UserProfilePostsView from "./UserProfilePostsView";

function UserProfile({ route, navigation }) {
  const [currentView, setCurrentView] = React.useState(UserProfilePostsView);
  const userInfo = route.params.userInfo;

  const onSettingsPressed = async (data) => {
    navigation.navigate("Settings");
  };

  const [modalVisible, setModalVisible] = React.useState(false);
  const [selectedImage, setSelectedImage] = React.useState(null);

  const toggleModalView = () => {
    setModalVisible(!modalVisible);
  };

  const updateSelectedImage = (item) => {
    setSelectedImage(item);
  };

  const onPress = (data) => {
    switch (data["_targetInst"]["alternate"]["memoizedProps"]["name"]) {
      case "postsButton":
        setCurrentView(UserProfilePostsView);
        return;
      case "workoutsButton":
        setCurrentView(UserProfileWorkoutsView);
        return;
      case "dietButton":
        setCurrentView(UserProfileDietView);
        return;
      case "galleryButton":
        setCurrentView(() => (
          <UserProfileGalleryView
            toggleModalView={toggleModalView}
            updateSelectedImage={updateSelectedImage}
            modalVisible={modalVisible}
            selectedImage={selectedImage}
          />
        ));
        return;
    }
  };

  return (
    <SafeAreaView
      style={styles.container}
      contentInsetAdjustmentBehavior="never"
    >
      <View style={styles.profileHeader}>
        <Pressable style={styles.profilePic} onPress={() => {}}>
          <Image
            style={{ width: 100, height: 100, resizeMode: "contain" }}
            source={require("./assets/profile_pic_temp.png")}
          />
        </Pressable>
        <View>
          <Text style={{ fontWeight: "bold" }}>
            STREAK {userInfo.userStreak} 🔥
          </Text>
          <Text style={{ fontWeight: "bold", fontSize: 24 }}>
            {userInfo.userFirstLastName}
          </Text>
          <Text style={{}}>{userInfo.userBio}</Text>
        </View>
      </View>

      <View style={styles.tabBar}>
        <Pressable
          style={styles.tabBarIcon}
          name="postsButton"
          onPress={onPress}
        >
          <Image
            style={styles.tabBarIcon}
            source={require("./assets/posts_icon.png")}
          />
        </Pressable>
        <Pressable
          style={styles.tabBarIcon}
          name="workoutsButton"
          onPress={onPress}
        >
          <Image
            style={styles.tabBarIcon}
            source={require("./assets/barbell-hand.png")}
          />
        </Pressable>
        <Pressable
          style={styles.tabBarIcon}
          name="dietButton"
          onPress={onPress}
        >
          <Image
            style={styles.tabBarIcon}
            source={require("./assets/diet_icon.png")}
          />
        </Pressable>
        <Pressable
          style={styles.tabBarIcon}
          name="galleryButton"
          onPress={onPress}
        >
          <Image
            style={styles.tabBarIcon}
            source={require("./assets/gallery_icon.png")}
          />
        </Pressable>
      </View>

      <Pressable style={styles.settingsButton} onPress={onSettingsPressed}>
        <Image
          style={{ width: 40, height: 40, resizeMode: "contain" }}
          source={require("./assets/settings_cog.png")}
        />
      </Pressable>

      <View style={styles.contentContainer}>{currentView}</View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "dodgerblue",
    alignItems: "center",
    justifyContent: "flex-start",
  },
  settingsButton: {
    position: "absolute",
    alignSelf: "flex-end",
    height: 40,
    width: 40,
    marginTop: 60,
    paddingRight: 60,
  },
  profileHeader: {
    alignItems: "flex-start",
    flexDirection: "row",
    marginTop: 20,
    width: "100%",
    marginHorizontal: 80,
  },
  profilePic: {
    alignSelf: "flex-start",
    marginRight: 10,
    marginLeft: 10,
  },
  tabBar: {
    height: 40,
    flexDirection: "row",
    justifyContent: "space-between",
    width: "100%",
    backgroundColor: "lightblue",
    alignItems: "center",
    marginTop: 20,
  },
  tabBarIcon: {
    flex: 1,
    alignSelf: "center",
    width: 30,
    height: 30,
    resizeMode: "contain",
  },
  contentContainer: {
    flex: 1,
    width: "100%",
    //backgroundColor: "white",
  },
});

export default UserProfile;

UserProfileGalleryView (child component):

import React, { useEffect } from "react";
import {
  StyleSheet,
  Image,
  FlatList,
  Dimensions,
  Modal,
  TouchableOpacity,
  View,
} from "react-native";

//temp for displaying purposes
const userImages = new Array(17).fill("https://picsum.photos/400");
const screenWidth = Dimensions.get("window").width;
const numColumns = 3;
const tileSize = screenWidth / numColumns;

function renderItem({ item, props }) {
  //Single item of Grid
  return (
    <TouchableOpacity
      key={item.id}
      style={{ height: tileSize, width: tileSize }}
      onPress={() => {
        props.toggleModalView();
        props.updateSelectedImage(item);
      }}
    >
      <Image resizeMode="cover" style={{ flex: 1 }} source={{ uri: item }} />
    </TouchableOpacity>
  );
}

function UserProfileGalleryView(props) {

  if (props.modalVisible) {
    //Modal to show full image with close button
    return (
      <Modal
        transparent={false}
        animationType={"fade"}
        visible={props.modalVisible}
        onRequestClose={() => {
          props.toggleModalView();
        }}
      >
        <View style={styles.modelStyle}>
          <Image
            style={styles.fullImageStyle}
            source={{ uri: props.selectedImage }}
          />
          <TouchableOpacity
            activeOpacity={0.5}
            style={styles.closeButtonStyle}
            onPress={() => {
              props.toggleModalView();
            }}
          >
            <Image
              source={{
                uri: "https://cdn2.iconfinder.com/data/icons/media-controls-5/100/close-1024.png",
              }}
              style={{ width: 25, height: 25, marginTop: 16 }}
            />
          </TouchableOpacity>
        </View>
      </Modal>
    );
  } else {
    return (
      <FlatList
        data={userImages}
        renderItem={({ item }) =>
          renderItem({
            item,
            props,
          })
        }
        numColumns={3}
      />
    );
  }
}

const styles = StyleSheet.create({
  photoGrid: {
    flex: 3,
    marginHorizontal: "auto",
    backgroundColor: "dodgerblue",
  },
  row: {
    flexDirection: "row",
  },
  col: {
    backgroundColor: "dodgerblue",
    borderColor: "#fff",
    borderWidth: 1,
    flex: 3,
  },
  galleryImage: {
    width: "100%",
    resizeMode: "contain",
  },

  containerStyle: {
    justifyContent: "center",
    flex: 1,
    marginTop: 20,
  },
  fullImageStyle: {
    justifyContent: "center",
    alignItems: "center",
    height: "100%",
    width: "98%",
    resizeMode: "contain",
  },
  modelStyle: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "rgba(0,0,0,0.4)",
  },
  closeButtonStyle: {
    width: 25,
    height: 25,
    top: 50,
    right: 20,
    position: "absolute",
  },
});

export default UserProfileGalleryView;

2

Answers


  1. I agree with the comment above it’s not recommended to store components in state.

    That being said I believe the issue you are having is inside of your onPress event when you set the current view you aren’t flipping the modal state as well for your gallery modal. So the view is set but the state value for modalVisible = false That is then passed to your child component and the modal isn’t shown.

    I think this will fix your problem:

      const onPress = (data) => {
        switch (data["_targetInst"]["alternate"]["memoizedProps"]["name"]) {
          case "postsButton":
            setCurrentView(UserProfilePostsView);
            return;
          case "workoutsButton":
            setCurrentView(UserProfileWorkoutsView);
            return;
          case "dietButton":
            setCurrentView(UserProfileDietView);
            return;
          case "galleryButton":
            setModalVisible(true)     <------- Add this line
            setCurrentView(() => (
              <UserProfileGalleryView
                toggleModalView={toggleModalView}
                updateSelectedImage={updateSelectedImage}
                modalVisible={modalVisible}
                selectedImage={selectedImage}
              />
            ));
            return;
        }
      };
    

    Adding the line setModalVisible(true) should make the modal be show when you first set the view but allow you to hide it like you currently have setup.

    Login or Signup to reply.
  2. To provide a proper answer / explanation for my comment.

    You can use the onPress function to set a string for the currentView and based on that state you can render you components.

    function UserProfile({ route, navigation }) {
      const [currentView, setCurrentView] = React.useState("posts");
    
      // ...
    
      const onPress = (newView) => {
        setCurrentView(newView);
      };
    
      return (
        <SafeAreaView
          style={styles.container}
          contentInsetAdjustmentBehavior="never"
        >
          ...
    
          <View style={styles.tabBar}>
            <Pressable
              style={styles.tabBarIcon}
              onPress={() => setCurrentView("posts")}
            >
              <Image
                style={styles.tabBarIcon}
                source={require("./assets/posts_icon.png")}
              />
            </Pressable>
            <Pressable
              style={styles.tabBarIcon}
              onPress={() => onPress("workouts")}
            >
              <Image
                style={styles.tabBarIcon}
                source={require("./assets/barbell-hand.png")}
              />
            </Pressable>
            <Pressable style={styles.tabBarIcon} onPress={() => onPress("diet")}>
              <Image
                style={styles.tabBarIcon}
                source={require("./assets/diet_icon.png")}
              />
            </Pressable>
            <Pressable style={styles.tabBarIcon} onPress={() => onPress("gallery")}>
              <Image
                style={styles.tabBarIcon}
                source={require("./assets/gallery_icon.png")}
              />
            </Pressable>
          </View>
    
          <Pressable style={styles.settingsButton} onPress={onSettingsPressed}>
            <Image
              style={{ width: 40, height: 40, resizeMode: "contain" }}
              source={require("./assets/settings_cog.png")}
            />
          </Pressable>
          <View style={styles.contentContainer}>
            {currentView === "posts" ? (
              <UserProfilePostsView />
            ) : currentView === "workouts" ? (
              <UserProfileWorkoutsView />
            ) : currentView === "diet" ? (
              <UserProfileDietView />
            ) : currentView === "gallery" ? (
              <UserProfileGalleryView
                toggleModalView={toggleModalView}
                updateSelectedImage={updateSelectedImage}
                modalVisible={modalVisible}
                selectedImage={selectedImage}
              />
            ) : null}
          </View>
        </SafeAreaView>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search