我有此页面,其中显示单个帖子,并且我有一个点赞按钮。如果喜欢该帖子,则当用户单击该按钮时,它将其状态更改为不相似按钮,但是如果不喜欢该帖子,则将注册类似对象,并且将id推入数组,但是按钮状态没有更新,我必须重新加载页面才能看到该页面。有人可以告诉我如何解决此问题吗?
这是代码:
const [liked, setLiked] = useState(false)
const [data, setData] = useState([]);
function likePosts(post, user) {
post.likes.push({ id: user });
setData(post);
axiosInstance.post('api/posts/' + post.slug + '/like/');
window.location.reload()
}
function unlikePosts(post, user) {
console.log('unliked the post');
data.likes = data.likes.filter(x => x.id !== user);
setData(data);
return (
axiosInstance.delete('api/posts/' + post.slug + '/like/')
)
}
对于按钮:
{data.likes && data.likes.find(x => x.id === user) ?
(<FavoriteRoundedIcon style={{ color: "red" }}
onClick={() => {
unlikePosts(data, user)
setLiked(() => liked === false)
}
}
/>)
: (<FavoriteBorderRoundedIcon
onClick={() => {
likePosts(data, user)
setLiked(() => liked === true)
}
}
/>)
}
谢谢,请问是否需要更多详细信息。
答案 0 :(得分:2)
正如@iz_在评论中指出的那样,您的主要问题是您直接在改变状态而不是调用setState
函数。
为清楚起见,我将data
重命名为post
,因为您已经说过,这是一个代表一个帖子数据的对象。
const [post, setPost] = useState(initialPost);
您不需要使用liked
作为状态,因为我们已经可以通过查看我们的用户是否在post
数组中来从post.likes
数据访问此信息。这使我们可以拥有“单一事实来源”,而我们只需要在一个地方进行更新即可。
const isLiked = post.likes.some((like) => like.id === user.id);
我对likes
数组感到困惑。好像是一组对象{id: number}
,在这种情况下,您应该只拥有一组喜欢该帖子的用户的ID。但是对象中可能还有其他属性(例如用户名或时间戳)。
在为诸如博客文章之类的复杂事物设计组件时,您希望分解一些小片段,以便在应用程序的其他地方使用。我们可以定义一个LikeButton
来显示我们的内心。这是一个“表示”组件,不处理任何逻辑。它只需要知道帖子isLiked
和做什么onClick
。
export const LikeButton = ({ isLiked, onClick }) => {
const Icon = isLiked ? FavoriteRoundedIcon: FavoriteBorderRoundedIcon;
return (
<Icon
style={{ color: isLiked ? "red" : "gray" }}
onClick={onClick}
/>
);
};
我们关于喜好和不喜好的许多逻辑可能会分解为某种usePostLike
钩子,但是我还没有完全优化它,因为我不知道您的API在做什么以及我们如何应该回应我们得到的回应。
当用户单击“赞”按钮时,我们希望更改立即反映在UI中,因此我们调用setPost
并从likes
数组中添加或删除当前用户。我们必须使用新对象设置状态,因此我们将复制所有未使用传播运算符...post
更改的post属性,然后使用已编辑的版本覆盖likes
属性。 filter()
和concat()
都是安全的数组函数,它们返回数组的新副本。
我们还需要调用API来发布更改。您在“ like”和“ unlike”场景中都使用相同的url,因此无需调用axios.post
和axios.delete
,我们可以调用通用函数axios.request
并传递方法名称'post'
或'delete'
作为config
对象的参数。 [axios docs]我们可能可以以相似的方式合并两个setPost
调用,并将likePost()
和unlikePost()
更改为一个toggleLikePost()
函数。但是现在,这就是我所拥有的:
export const Post = ({ initialPost, user }) => {
const [post, setPost] = useState(initialPost);
const isLiked = post.likes.some((like) => like.id === user.id);
function likePost() {
console.log("liked the post");
// immediately update local state to reflect changes
setPost({
...post,
likes: post.likes.concat({ id: user.id })
});
// push changes to API
apiUpdateLike("post");
}
function unlikePost() {
console.log("unliked the post");
// immediately update local state to reflect changes
setPost({
...post,
likes: post.likes.filter((like) => like.id !== user.id)
});
// push changes to API
apiUpdateLike("delete");
}
// generalize like and unlike actions by passing method name 'post' or 'delete'
async function apiUpdateLike(method) {
try {
// send request to API
await axiosInstance.request("api/posts/" + post.slug + "/like/", { method });
// handle API response somehow, but not with window.location.reload()
} catch (e) {
console.log(e);
}
}
function onClickLike() {
if (isLiked) {
unlikePost();
} else {
likePost();
}
}
return (
<div>
<h2>{post.title}</h2>
<div>{post.likes.length} Likes</div>
<LikeButton onClick={onClickLike} isLiked={isLiked} />
</div>
);
};