Freddie Banfield
Freddie Banfield

Reputation: 33

How to re-render child component when parents state changes - React Native?

So I am trying to adjust the position of a view. In doing so I have two components within a parent. One component is the navbar. It has buttons that change the content on the page. The other is the ContentBoard, this will display the different content. The ContentBoard also has a tab that needs to relocate to the correct button.

Where I'm stuck is that I have managed to get the navbar to change the tabID state in the parent, but then it should rerender the ContentBoard as it had the prop tabID. I'm just confused as to why when I send the state as a prop it is not automatically reloading the component since the value has changed.

Here is the code: Dashboard

  const Dashboard = ({ navigation }) => {
  const [email, setEmail] = useState("");
  const [tabID, setTabID] = useState("2");

  const user = auth.currentUser;

  useEffect(() => {
    if (user != null) {
      user.providerData.forEach((userInfo) => {
        setEmail(userInfo.email);
      });
    }
  }, []);

  function changeTab(id) {
    setTabID(id);
  }

  return (
    <KeyboardAwareScrollView
      scrollEnabled={false}
      behavior="padding"
      contentContainerStyle={{ flex: 1 }}
    >
      <View style={{ flex: 1 }}>
        <ImageBackground source={background} style={styles.backgroundImg}>
          <ContentBoard activeBtn={tabID} />
          <Navbar activeBtn={tabID} onPress={changeTab} />
        </ImageBackground>
      </View>
    </KeyboardAwareScrollView>
  );
};

Navbar

export default function Navbar(props) {
  const activeBtnColor = "#53c5d6";
  const regularBtnColor = "#00495f";
  const activeBtn = props.activeBtn;

  const styles = StyleSheet.create({
    navbar: {
      position: "absolute",
      backgroundColor: "white",
      opacity: 0.5,
      width: "100%",
      height: 100,
      bottom: 0,
    },
    buttonContainer: {
      flexDirection: "row",
      width: "100%",
      height: "100%",
      justifyContent: "center",
      alignItems: "center",
    },
    button: {
      justifyContent: "center",
      alignItems: "center",
      borderRadius: 10,
      width: 60,
      height: 60,
      margin: 5,
      shadowColor: "black",
      shadowRadius: 4,
      shadowOpacity: 1,
      shadowOffset: { width: 0, height: 4 },
      elevation: 8,
    },
    familyTree: {
      backgroundColor: `${activeBtn == 0 ? activeBtnColor : regularBtnColor}`,
    },
    calendar: {
      backgroundColor: `${activeBtn == 1 ? activeBtnColor : regularBtnColor}`,
    },
    home: {
      backgroundColor: `${activeBtn == 2 ? activeBtnColor : regularBtnColor}`,
    },
    memories: {
      backgroundColor: `${activeBtn == 3 ? activeBtnColor : regularBtnColor}`,
    },
    notifications: {
      backgroundColor: `${activeBtn == 4 ? activeBtnColor : regularBtnColor}`,
    },
    icon: {
      width: 50,
      height: 50,
    },
  });

  return (
    <View style={{ flex: 1 }}>
      <View style={styles.navbar}>
        <View style={styles.buttonContainer}>
          <TouchableOpacity
            style={[styles.familyTree, styles.button]}
            onPress={() => props.onPress("0")}
          >
            <Image
              style={styles.icon}
              source={require("./../../assets/buttons/tree-icon.png")}
            />
          </TouchableOpacity>
          <TouchableOpacity
            style={[styles.calendar, styles.button]}
            onPress={() => props.onPress("1")}
          >
            <Image
              style={styles.icon}
              source={require("./../../assets/buttons/calendar-icon.png")}
            />
          </TouchableOpacity>
          <TouchableOpacity
            style={[styles.home, styles.button]}
            onPress={() => props.onPress("2")}
          >
            <Image
              style={styles.icon}
              source={require("./../../assets/buttons/home-icon.png")}
            />
          </TouchableOpacity>
          <TouchableOpacity
            style={[styles.memories, styles.button]}
            onPress={() => props.onPress("3")}
          >
            <Image
              style={styles.icon}
              source={require("./../../assets/buttons/folder-icon.png")}
            />
          </TouchableOpacity>
          <TouchableOpacity
            style={[styles.notifications, styles.button]}
            onPress={() => props.onPress("4")}
          >
            <Image
              style={styles.icon}
              source={require("./../../assets/buttons/bell-icon.png")}
            />
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );
}

ContentBoard

export default function ContentBoard(props) {
  let tabOffset = 0;

  let activeBtn = props.activeBtn;
  useEffect(() => {
    switch (activeBtn) {
      case 0:
        tabOffset = 140;
        break;
      case 1:
        tabOffset = 70;
        break;
      case 2:
        tabOffset = 0;
        break;
      case 3:
        tabOffset = -70;
        break;
      case 4:
        tabOffset = -140;
        break;
    }
  }, [activeBtn]);

  const styles = StyleSheet.create({
    contentContainer: {
      justifyContent: "center",
      alignItems: "center",
      backgroundColor: "white",
      alignSelf: "center",
      width: 350,
      height: 650,
      top: 40,
      borderRadius: 20,
    },
    tab: {
      position: "relative",
      backgroundColor: "white",
      alignSelf: "center",
      borderRadius: 10,
      right: tabOffset,
      top: 12,
      width: 70,
      height: 135,
      zIndex: -1,
    },
  });

  return (
    <View>
      <View style={styles.contentContainer}></View>
      <View style={[styles.tab]}></View>
    </View>
  );

I appreciate any help and would love to learn why this is not working, thanks.

Upvotes: 1

Views: 707

Answers (1)

Freddie Banfield
Freddie Banfield

Reputation: 33

I worked it out. What I ended up doing was creating a hook in the primary to store the offset, pass the setOffset to the child component via a function. Have the offset changed in the child component, at the same time the offset is also passed by props to the child component to which it updates the style of the tab.

In short, I created a hook at the parent level that the child manipulates.

ContentBoard

    const ContentBoard = (props) => {
  useEffect(() => {
    switch (props.activeBtn) {
      case 0:
        props.changeTabOffset(140);
        break;
      case 1:
        props.changeTabOffset(70);
        break;
      case 2:
        props.changeTabOffset(0);
        break;
      case 3:
        props.changeTabOffset(-70);
        break;
      case 4:
        props.changeTabOffset(-140);
        break;
    }
    console.log("activeBtn: " + props.activeBtn);
  }, [props.activeBtn]);

  const styles = StyleSheet.create({
    contentContainer: {
      justifyContent: "center",
      alignItems: "center",
      backgroundColor: "white",
      alignSelf: "center",
      width: 350,
      height: 650,
      top: 40,
      borderRadius: 20,
    },
    tab: {
      position: "relative",
      backgroundColor: "white",
      alignSelf: "center",
      borderRadius: 10,
      right: props.offset,
      top: 12,
      width: 70,
      height: 135,
      zIndex: -1,
    },
  });

  return (
    <View>
      <View style={styles.contentContainer}></View>
      <View style={styles.tab}></View>
    </View>
  );
};

Dashboard

const Dashboard = ({ navigation }) => {
  const [email, setEmail] = useState("");
  const [tabID, setTabID] = useState(2);
  const [tabOffset, setTabOffset] = useState(140);

  const user = auth.currentUser;

  useEffect(() => {
    if (user != null) {
      user.providerData.forEach((userInfo) => {
        setEmail(userInfo.email);
      });
    }
  }, []);

  function changeTab(id) {
    setTabID(id);
  }

  function changeTabOffset(offset) {
    setTabOffset(offset);
  }

  return (
    <KeyboardAwareScrollView
      scrollEnabled={false}
      behavior="padding"
      contentContainerStyle={{ flex: 1 }}
    >
      <View style={{ flex: 1 }}>
        <ImageBackground source={background} style={styles.backgroundImg}>
          <ContentBoard
            activeBtn={tabID}
            offset={tabOffset}
            changeTabOffset={changeTabOffset}
          />
          <Navbar activeBtn={tabID} onPress={changeTab} />
        </ImageBackground>
      </View>
    </KeyboardAwareScrollView>
  );
};

const styles = StyleSheet.create({
  backgroundImg: {
    display: "flex",
    flex: 1,
    width: null,
    height: null,
  },
});

Upvotes: 1

Related Questions