Javascript 返回时对重复数据作出本机和UseFocuseEffect反应
我正在与UseFocuseEffect或useEffect斗争。我试图获取数据时,用户导航到用户配置文件这是工作,但当用户导航回配置文件,我有一个重复的帖子!我已经研究这个问题三天了。请告诉我你有什么想法 代码如下: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
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]);