Search code examples
javascriptreactjsreact-nativereact-native-flatlistuirefreshcontrol

how to refresh a flatlist component from a screen when going back to the screen with new data fetched (React Native)


I'm trying to do an app where you can create some projects, to do so on the main screen ("ProjectsScreen") I have a <ProjectsList/> component that takes the data from fetchProjects when rendered.

The problem comes after a new project is created. To create a project im navigating to "CreateProjectScreen" where, when the new project is created, I'm nagivating back to "ProjectsScreen" the <ProjectsList/> is not updating with the new data.

The new data after creating a project its saved correctly but the FlatList is not refreshed, at this moment I've achieved so with a scroll-to-refresh

How can I refresh the FlatList when I am back on ProjectScreen

Thats the structure of the code

-ProjectsScreen
--ProjectsList
---FetchProjects
-CreateProjectScreen

I add the code bellow

ProjectsScreen:

const ProjectsScreen = ({ navigation }) => {
  const [apiResult, setApiResult] = useState(null);
  const [apiError, setApiError] = useState(null);
  const { reloadProjects } = useProjects();
  const { t } = useTranslation();

  useEffect(() => {
    reloadProjects();
  }, []);

  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>{t("title.projects")}</Text>
      <View style={styles.viewContainer}>
        <View style={styles.projectsContainer}>
          <ProjectsList navigation={navigation} />
        </View>
        <View style={styles.buttonContainer}>
          <StyledButton
            whiteBtn={true}
            title={t("buttons.new_project")}
            navigation={navigation}
            onPress={() => navigation.navigate("CreateProject")}
          />
        </View>
      </View>
    </SafeAreaView>
  );
};

Project list component:

const ProjectsList = ({ navigation }) => {
  const { projects, isLoading, reloadProjects } = useProjects();
  const [refreshing, setRefreshing] = useState(false);

  const onRefresh = useCallback(() => {
    setRefreshing(true);
    setTimeout(() => {
      reloadProjects();
      setRefreshing(false);
    }, 2000);
  }, []);

  return (
    <View style={styles.container}>
      {isLoading ? (
        <ActivityIndicator size="large" colors={COLORS.primary} />
      ) : (
        <FlatList
          data={projects}
          keyExtractor={(project) => project.uuid}
          renderItem={({ item: project }) => (
            <TouchableOpacity
              onPress={() => navigation.navigate("Upload", { project })}
            >
              <View style={styles.projectContainer}>
                <View
                  style={{
                    flexDirection: "row",
                    justifyContent: "space-between",
                  }}
                >
                  <Text style={styles.title}>{project.name}</Text>
                  <TouchableOpacity
                    onPress={() => handleDeletePress(project, reloadProjects)}
                  >
                    <Image style={{ marginTop: 12 }} source={icons.del} />
                  </TouchableOpacity>
                </View>
                <Text style={styles.description}>{project.description}</Text>
                {/* <Text>UUID: {project.uuid}</Text> */}
                {project.models && project.models.length > 0 && (
                  <View style={styles.modelsContainer}>
                    {/* <Text style={styles.modelsHeader}>Models:</Text> */}
                    <FlatList
                      data={project.models}
                      keyExtractor={(model) => model.uuid}
                      renderItem={({ item: model }) => (
                        <View style={styles.modelContainer}>
                          {/* <LinearGradient colors={['#FF4D00', '#FF00D6']} style={styles.gradient}> */}
                          <Text style={styles.modelsName}>{model.name}</Text>
                          <Text style={styles.modelsDesc}>
                            {model.description}
                          </Text>
                          {/* <Text>UUID: {model.uuid}</Text> */}
                          {/* </LinearGradient> */}
                        </View>
                      )}
                      showsHorizonralScrollIndicator={false}
                      horizontal
                    />
                  </View>
                )}
              </View>
            </TouchableOpacity>
          )}
          showsVerticalScrollIndicator={false}
          refreshing={refreshing} // Added pull to refesh state
          onRefresh={onRefresh} // Added pull to refresh control
          ListEmptyComponent={
            <Text style={{ fontSize: SIZES.medium }}>
              {t("error.projects")} :( {"\n\n"}
              {t("error.refresh")}
            </Text>
          }
        />
      )}
    </View>
  );
};

fetchProjects:

const useProjects = () => {
  const [projects, setProjects] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  const fetchProjects = async () => {
    setIsLoading(true);
    const token = await AsyncStorage.getItem("access_token");
    const config = {
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    };

    try {
      const response = await axios.get("http:/asof/v1/projects/", config);

      if (!response.status === 200) {
        throw new Error(`HTTP error! Status: ${response.status}`);
      } else {
        setProjects(response.data);
      }
    } catch (error) {
      console.log(error);
      console.error("Error fetching repositories:", error.message);
    } finally {
      setIsLoading(false);
    }
  };

  useEffect(() => {
    fetchProjects();
  }, []);
  return { projects, isLoading, reloadProjects: fetchProjects };
};

export default useProjects;

CreateProjectScreen:

const initialValues = {
  name: "",
  description: "",
  models_uuid: [],
};
const sendApi = async (
  values,
  setApiResult,
  setApiError,
  { navigation },
  reloadProjects
) => {
  try {
    // Obtén el token de AsyncStorage
    const token = await AsyncStorage.getItem("access_token");

    // Configuración de la solicitud
    const config = {
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    };

    // Datos del cuerpo de la solicitud
    const data = {
      name: values.name,
      description: values.description,
      models_uuid: values.models_uuid,
    };

    // Realiza la solicitud POST
    const response = await axios.post("http://asof/v1/projects/", data, config);

    // Maneja la respuesta
    if (response.status === 201) {
      console.log("Project created successfully");
      Alert.alert("Project created successfully");
      navigation.navigate("ProjectsScreen");
      reloadProjects();
      // Si es necesario, puedes manejar el resultado de la API
      setApiResult(response.data);
    } else {
      console.log(response);
      setApiError("Error al crear el proyecto");
    }
  } catch (error) {
    setApiError(error.message);
    console.log("Error en la solicitud POST:", error);
  }
};
function CreateProjectScreen({ navigation }) {
  const [apiResult, setApiResult] = useState(null);
  const [apiError, setApiError] = useState(null);
  const { login } = useContext(AuthContext);
  const { reloadProjects } = useProjects(); // Obtén reloadProjects del hook useProjects
  const [selectedModelUUIDs, setSelectedModelUUIDs] = useState([]);
  const { t } = useTranslation();

  const handleModelsSelect = (modelUUIDs) => {
    setSelectedModelUUIDs(modelUUIDs);
    console.log("Modelos seleccionados: ", selectedModelUUIDs);
  };
  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>{t("title.create")}</Text>
      <Formik
        validationSchema={ProjectValidationSchema}
        initialValues={initialValues}
        onSubmit={async (values) => {
          values.models_uuid = selectedModelUUIDs;
          console.log(values);
          console.log("Submitting project:", values);
          await sendApi(
            values,
            setApiResult,
            setApiError,
            { navigation },
            reloadProjects
          );
        }}
      >
        {({ values, handleChange, handleSubmit }) => {
          const { name, description, password, repeatPassword } = values;
          return (
            <View style={styles.container}>
              <Text style={styles.text}>{t("name")}</Text>
              <FormikInputValue
                value={name}
                onChangeText={handleChange("name")}
                name="name"
                placeHolder={t("name")}
                image={icons.user}
              />
              <Text style={styles.text}>{t("description")}</Text>
              <FormikInputValue
                value={description}
                onChangeText={handleChange("description")}
                name="description"
                placeHolder={t("description")}
                image={icons.mail}
              />
              <ModelsList onModelsSelect={handleModelsSelect} />
              <View style={styles.loginButton}>
                {apiError && (
                  <Text style={{ color: "#ff0000" }}>
                    {t("error.api_error")}: {apiError}
                  </Text>
                )}
                <StyledButton
                  onPress={handleSubmit}
                  title={t("buttons.create")}
                  whiteBtn={true}
                />
              </View>
            </View>
          );
        }}
      </Formik>
    </SafeAreaView>
  );
}

I'm successfully getting the new data after adding anew project but no clue on how to refresh the component from main screen.


Solution

  • You can use useFocusEffect and useCallback to re-refetch data everytime screen is in focus.

     useFocusEffect(
        useCallback(() => {
            refetchSomeData();
        }, []),
    );