Javascript 返回时对重复数据作出本机和UseFocuseEffect反应

Javascript 返回时对重复数据作出本机和UseFocuseEffect反应,javascript,reactjs,react-native,react-navigation,Javascript,Reactjs,React Native,React Navigation,我正在与UseFocuseEffect或useEffect斗争。我试图获取数据时,用户导航到用户配置文件这是工作,但当用户导航回配置文件,我有一个重复的帖子!我已经研究这个问题三天了。请告诉我你有什么想法 代码如下: import React, { useState, useEffect, useRef } from "react"; import { View, Text, FlatList, Image, TouchableOpacity, Ani

我正在与UseFocuseEffect或useEffect斗争。我试图获取数据时,用户导航到用户配置文件这是工作,但当用户导航回配置文件,我有一个重复的帖子!我已经研究这个问题三天了。请告诉我你有什么想法

代码如下:

import React, { useState, useEffect, useRef } from "react";
import {
  View,
  Text,
  FlatList,
  Image,
  TouchableOpacity,
  Animated,
  ActivityIndicator,
} from "react-native";
import APIKIT from "../../../services/APIKit";
import styles from "./UserPostStyle";
import moment from "moment";
import { MaterialCommunityIcons, FontAwesome } from "@expo/vector-icons";
import { useNavigation } from "@react-navigation/native";
import { useFocusEffect } from "@react-navigation/native";

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
moment.updateLocale("en", {
  relativeTime: {
    future: "in %s",
    past: "%s ago",
    s: "Just now",
    ss: "%ss",
    m: "1m",
    mm: "%dm",
    h: "1h",
    hh: "%dh",
    d: "1d",
    dd: "%dd",
    M: "1mo",
    MM: "%dmo",
    y: "1y",
    yy: "%dy",
  },
});
export function UserPosts({ ...props }) {
  const [userPosts, setUserPosts] = useState([]);
  const [posts, setPosts] = useState([]); // Array with the posts objects
  let postsArray = useRef(posts).current; // Auxiliar array for storing posts before updating the state. Pd: this will not re-render the component as it is a reference
  const [page, setPage] = useState(1);
  const [lastPage, setLastPage] = useState(0);
  const [loadingMore, setLoadingMore] = useState(false);
  const navigation = useNavigation();

  useFocusEffect(
    React.useCallback(() => {
      if (props.userId) {
        fetchPosts();
      }
      return () => {};
    }, [props.userToken, props.userId, page])
  );

  // useEffect(() => {
  //   console.log(" -----------useEffect page", page);
  //   console.log(" -----------useEffect lastPage", lastPage);
  //   let mounted = true;
  //   if (mounted && props.userId) {
  //     fetchPosts();
  //   }
  //   return () => {
  //     console.log("return useEffect page", page);
  //     console.log("return useEffect lastPage", lastPage);
  //     mounted = false;
  //   };
  // }, [props.userToken, props.userId, page]);

  const fetchPosts = async () => {
    if (props.userToken !== null) {
      const config = {
        headers: { Authorization: `Bearer ${props.userToken}` },
      };
      APIKIT.get(`/user/posts/${props.userId}?page=${page}`, config)
        .then((response) => {
          let posts = [];
          const { data } = response;
          setLastPage(data.userPosts.last_page);

          for (let userPost of data.userPosts.data) {
            let post = {};
            post["id"] = userPost.id;
            post["location_coordinate"] = userPost.location_coordinate;
            post["location_icon"] = userPost.location_icon;
            post["location_id"] = userPost.location_id;
            post["location_title"] = userPost.location_title;
            post["location_vicinity"] = userPost.location_vicinity;
            post["post_body"] = userPost.post_body;
            post["created_at"] = moment(userPost.created_at).fromNow(true);
            post["user_id"] = userPost.user_id;
            posts.push(post);
          }
          posts.forEach((newPost) => {
            // Add the new fetched post to the head of the posts list
            postsArray.unshift(newPost);
          });
          setPosts([...postsArray]); // Shallow copy of the posts array to force a FlatList re-render
          setUserPosts((prevPosts) => [...prevPosts, ...posts]);
          setLoadingMore(false);
          if (page === lastPage) {
            setLoadingMore(false);
            return;
          }
        })
        .catch((e) => {
          console.log("There is an error eccured while getting the posts ", e);
          setLoadingMore(false);
        });
    }
  };

  const Item = ({
    post_body,
    id,
    location_coordinate,
    location_icon,
    location_title,
    location_vicinity,
    location_id,
    created_at,
  }) => (
    <View style={styles.postContainer}>
      <TouchableOpacity
        onPress={() => {
          navigation.navigate("PostDetailsScreen", {
            location_coordinate: JSON.parse(location_coordinate),
            userAvatar: props.userAvatar,
            username: props.username,
            name: props.name,
            created_at: created_at,
            post_body: post_body,
            location_title: location_title,
            location_vicinity: location_vicinity,
            location_icon: location_icon,
            location_id: location_id,
          });
        }}
      >
        <View style={styles.postHeader}>
          <Image
            style={styles.userAvatar}
            source={
              props.userAvatar
                ? { uri: props.userAvatar }
                : require("../../../../assets/images/default.jpg")
            }
          />

          <View style={styles.postUserMeta}>
            <View style={{ flexDirection: "row", alignItems: "center" }}>
              <Text style={styles.name}>{props.name}</Text>
              <Text style={styles.createdAt}>{created_at}</Text>
            </View>
            <Text style={styles.username}>@{props.username}</Text>
          </View>
        </View>
        <View style={styles.postContent}>
          <View>
            <Text style={styles.postBody}>{post_body}</Text>
          </View>
        </View>
      </TouchableOpacity>
      <TouchableOpacity
        style={styles.locationInfoContainer}
        onPress={() => {
          navigation.navigate("PostPlaceDetailsScreen", {
            location_coordinate: JSON.parse(location_coordinate),
            userAvatar: props.userAvatar,
            username: props.username,
            name: props.name,
            created_at: created_at,
            post_body: post_body,
            location_title: location_title,
            location_vicinity: location_vicinity,
            location_icon: location_icon,
            location_id: location_id,
          });
        }}
      >
        <View style={styles.locationInfo}>
          <Image style={styles.locationIcon} source={{ uri: location_icon }} />
          <View style={styles.locationMeta}>
            <Text style={styles.locationTitle}>{location_title}</Text>
          </View>
        </View>
      </TouchableOpacity>
      <View
        style={{
          borderWidth: 1,
          borderColor: "#F2F2F2",
          marginTop: 10,
          marginBottom: 10,
        }}
      />
      <View style={styles.postFooter}>
        <TouchableOpacity>
          <MaterialCommunityIcons name="comment" size={24} color="#999999" />
        </TouchableOpacity>
        <TouchableOpacity>
          <FontAwesome name="star" size={24} color="#999999" />
        </TouchableOpacity>
        {/* After fav color #FFBE5B */}
        <TouchableOpacity>
          <MaterialCommunityIcons
            name="dots-horizontal"
            size={24}
            color="#999999"
          />
        </TouchableOpacity>
      </View>
    </View>
  );
  const renderItem = ({ item }) => (
    <Item
      location_coordinate={item.location_coordinate}
      post_body={item.post_body}
      id={item.id}
      location_id={item.location_id}
      location_icon={item.location_icon}
      location_title={item.location_title}
      location_vicinity={item.location_vicinity}
      created_at={item.created_at}
    />
  );

  const emptyPosts = () => {
    return (
      <View style={styles.noPostsMessageContainer}>
        <View style={styles.messageContainer}>
          <Image
            style={styles.noPostMessageImage}
            source={require("../../../../assets/images/Logo.png")}
          />
          <Text style={styles.noPostMessageText}>No posts yet!</Text>
        </View>
      </View>
    );
  };

  const handleLoadingMore = () => {
    if (page === lastPage) {
      return;
    }
    setPage(page + 1);
    setLoadingMore(true);
  };

  return (
    <View style={styles.userPostContainer}>
      <AnimatedFlatList
        showsVerticalScrollIndicator={false}
        data={userPosts}
        renderItem={renderItem}
        keyExtractor={(item) => item.id.toString()}
        contentContainerStyle={{
          paddingTop: 250,
          paddingBottom: 100,
        }}
        scrollEventThrottle={16}
        onScroll={props.scrolling}
        ListEmptyComponent={emptyPosts}
        onEndReachedThreshold={0.5}
        onMomentumScrollEnd={() => handleLoadingMore()}
        ListFooterComponent={
          loadingMore && <ActivityIndicator size="large" animating />
        }
      />
    </View>
  );
}


我认为问题在于,当用户导航回配置文件时,状态仍然相同,而UseFocuseEffect再次获取相同的数据

setUserPosts((prevPosts) => [...prevPosts, ...posts]);

谢谢你的帮助。

我从未使用过“react navigation”中的这个钩子,但在文档中它说:

有时我们想在屏幕聚焦时产生副作用。一方 效果可能包括添加事件侦听器、获取 数据、更新文档标题等,而这可以通过 聚焦和模糊事件,不太符合人体工程学

因此,每次您关注特定的路由时,代码都将运行

当用户导航到此用户配置文件时,我正在尝试获取数据 正在工作,但当用户导航回配置文件时,我有一个 重复的帖子

你所做的是错误的,因为你没有给你的回帖分页,我的意思是,你需要一个指向你最后回帖的“指针”。。。这样可以避免获取已有的数据。此外,用户体验将非常快,因为您的数据库将提供更轻松的响应

无论如何,我建议您在React Native的“useEffect”钩子中运行此代码,并尝试实现一个db侦听器或带有分页的有限滚动。当你聚焦屏幕时,这将不起作用,但你将能够在用户每次刷新平面列表时获取数据,就像Instagram、Twitter、Netflix

看看这个:

如果在关注特定路由时确实需要获取数据,只需实现分页(在组件状态下保存获取的最后一篇文章的索引)

更新 抱歉,我没有看到您正在代码中进行分页。更新组件状态时可能会出现问题

试试这样:

const [posts, setPosts] = useState([]); // Array with the posts objects

let postsArray = useRef(posts).current; // Auxiliar array for storing posts before updating the state. Pd: this will not re-render the component as it is a reference


const loadMorePosts = () => {
  // ... stuff

  // const newPosts = Fetch posts
  
  newPosts.forEach((newPost) => {
     // Add the new fetched post to the head of the posts list
     postsArray.unshift(newPost);
  })


  // The last post will be at the top of the list
  setPosts([...postsArray]); // Shallow copy of the posts array to force a FlatList re-render
}

// setUserPosts((prevPosts) => [...prevPosts, ...posts]);

useFocusEffect每次屏幕聚焦时都会运行,您是否希望在用户导航回屏幕时更新?是的,我知道。是的,我想在用户每次导航到用户配置文件时获取数据。然后,您应该使用api中的数据设置POST,而不将其与以前的值合并。如果是这样的话,``setUserPosts(posts);`````````当我滚动到平面列表的末尾时,它不会添加更多的帖子,而是会替换当前的帖子@TeaDev我更新了我的答案,看看这个,希望它能为你们服务感谢你们的回应。我喜欢你的方法,我也尝试过。但这个解决方案的问题是,当我发布一篇新文章时,它会自动导航到用户配置文件,并显示新文章,但当我尝试导航到其他任何地方并再次返回配置文件时,最后一篇文章不会显示。它应该会显示。我最近实现了这一点,效果很好。只需确保在组件状态下保存数据,并且不要卸载它。我所做的只是db listener(用于最近的文章和检索前10篇文章)和无限滚动加载它们之间的组合。主要部分是偏移量(分页)和所有帖子的数组状态(每次我获取帖子时都会更新数组状态)。我正试图弄明白这一点,但运气不好。你能在我的问题中看到我的全部代码并告诉我我做错了什么吗,我是一个愚蠢的程序员:)为什么你有page和lastPage?仅仅执行APIKIT.get(
/user/posts/${props.userId}?page=${lastPage}
,config)就足够了吗??如果您打开一份包含代码重要部分的点心,我将不胜感激,这将使您更容易获得帮助。
setUserPosts((prevPosts) => [...prevPosts, ...posts]);
const [posts, setPosts] = useState([]); // Array with the posts objects

let postsArray = useRef(posts).current; // Auxiliar array for storing posts before updating the state. Pd: this will not re-render the component as it is a reference


const loadMorePosts = () => {
  // ... stuff

  // const newPosts = Fetch posts
  
  newPosts.forEach((newPost) => {
     // Add the new fetched post to the head of the posts list
     postsArray.unshift(newPost);
  })


  // The last post will be at the top of the list
  setPosts([...postsArray]); // Shallow copy of the posts array to force a FlatList re-render
}

// setUserPosts((prevPosts) => [...prevPosts, ...posts]);