简体   繁体   中英

React Component Not Re Rendering using Spread Operator

I've tried other solutions on this site, but I am not sure why this is not working. I have a response I am getting from a server each time a button is pressed. The response comes through fine and I am able to see it each time the button is pressed. I am receiving an array of objects from the server, and I am using the useState hook to set the state of a variable to keep track of the objects in the array. However, the component does not re render. Interestingly, if I add a console.log statement to see the contents of the state variable and then save the page, I can see that the state variable was updated properly. The component still does not re render though. The relevant code is here:

import React, { useEffect, useState } from "react";
import { StyleSheet, View, TouchableOpacity, Alert } from "react-native";
import tailwind from "tailwind-rn";
import colors from "../config/colors";
import useAuth from "../hooks/useAuth";
import Screen from "../components/Screen";
import AppText from "../components/AppText";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import { getData } from "../hooks/useCache";
import { useIsFocused } from "@react-navigation/native";
import ListScreen from "./ListScreen";

const ProfileScreen = () => {
  const { logout, user } = useAuth();
  const [likes, setLikes] = useState("");
  const [completed, setCompleted] = useState("");
  const [responseJson, setResponseJson] = useState(null);
  const isFocused = useIsFocused();

  useEffect(() => {
    const likesFunc = async() => {
    setLikes(await getData("likes"));
    setCompleted(await getData("eventsCompleted"));
    try {
      const response = await fetch(
        "server url here",
        {
          headers: { Authorization: "Bearer " + user.idToken },
        }
      );
      const responseJson = await response.json();
      setLikes(responseJson.likes);
      setCompleted(responseJson.eventsCompleted);
    } catch {
      Alert.alert("There has been an error processing your profile");
    }
  }
  likesFunc();
  }, []);

  //get voted events
  //run when questionsLeft is 0 to save num of calls
  useEffect(() => {
    const eventFunction = async() => {
        try {
          const response = await fetch(
            "server url here",
            {
              headers: { Authorization: "Bearer " + user.idToken },
            }
          )
          const res = await response.json();
          setResponseJson([...res]);
        
        } catch (error) {
          Alert.alert(
            "An error has occurred loading your questions. Close the app and try again."
          );
          console.log(error);
        }
      }
      eventFunction();
  }, [isFocused]);

  return (
    <Screen style={styles.bg}>
      <View
        style={[
          tailwind("w-full flex-row py-4 justify-center items-center top-0"),
          { justifyContent: "space-between" },
        ]}
      >
        <AppText style={{ color: colors.white, fontSize: 30, marginLeft: 5 }}>
          Hello, {user.displayName.split(" ")[0]}
        </AppText>
        <TouchableOpacity
          style={styles.logoutButton}
          onPress={() => {
            Alert.alert("Log Out", "Are you sure you want to log out?", [
              {
                text: "Yes",
                style: "destructive",
                onPress: logout,
              },
              {
                text: "Cancel",
                style: "cancel",
              },
            ]);
          }}
        >
          <MaterialCommunityIcons
            name="logout-variant"
            size={25}
            color={colors.primary}
          />
        </TouchableOpacity>
      </View>
      <View
        style={tailwind("w-full h-1/5 justify-center items-center")}
      >
        <View
          style={[
            tailwind("w-full flex-row p-10 justify-center"),
            { justifyContent: "space-between" },
          ]}
        >
          <View style={tailwind("justify-center items-center")}>
            <AppText style={{ textDecorationLine: "underline" }}>
              Total Likes
            </AppText>
            <AppText style={{ paddingVertical: 10 }}>{likes}</AppText>
          </View>

          <View style={tailwind("justify-center items-center")}>
            <AppText style={{ textDecorationLine: "underline" }}>
              Completed
            </AppText>
            <AppText style={{ paddingVertical: 10 }}>{completed}</AppText>
          </View>
        </View>
      </View>
      <View
        style={tailwind("w-full h-4/5 flex-1 items-center")}
      >
        {responseJson == null ? 
        <AppText style={tailwind("mt-10")}>
          Select events on the "Discover" page!
        </AppText>
        : 
          <ListScreen caller={{"sender": "profile", "json": responseJson}}/>
        }
      </View>
    </Screen>
  );
};

export default ProfileScreen;

const styles = StyleSheet.create({
  logoutButton: {
    color: colors.white,
    paddingTop: 20,
    paddingRight: 10,
  },
  bg: {
    flex: 1,
    backgroundColor: colors.black,
  },
});


Update: If I set the state twice in a row, it works:

setResponseJson(null);
setResponseJson([...res]);

However, this is somewhat buggy and not optimal. Leads me to think it still is a reference issue, but I am not sure why the spread operator technique does not fix this.

Three things are wrong/weird in your code. Not sure it will fix your problem but here are they:

1- Do not pass an async function to the useEffect . Instead, create an async function inside and call it:

useEffect(() => {
  const myFunc = async => {...};
  myFunc();
},[]);

2- You are receiving an array of objects from your api call, so why do you want to spread it? It would lead to multiple objects inside your state, which doesn't seems right. Just pass your res in your state as is or format it as you want.

3- You are mixing async/await pattern with the .then . Either use an async func and await the result:

const myFunc = async () => {
  const resPromise = await fetch(...);
  const json = await resPromise.json();
  ...
}

or only use the .then :

  const myFunc = () => {
  fetch(...)
    .then(data => data.json())
    .then(json => ...);
  ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM