基于api调用的反应js中的按钮不改变状态
Posted
技术标签:
【中文标题】基于api调用的反应js中的按钮不改变状态【英文标题】:Button not changing state in react js based on api call 【发布时间】:2021-02-08 01:46:28 【问题描述】:我有一个显示单个帖子的页面,我有一个赞按钮。如果帖子被点赞,当用户单击按钮时,它的状态会更改为不喜欢按钮,但如果帖子不被点赞,则会注册点赞并将 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)
/>)
谢谢,如果需要更多详细信息,请询问。
【问题讨论】:
你不应该直接改变状态;使用setData
。
嘿,你能解释一下吗?
我无法真正测试您的代码,但您分配给 data.likes
不应该这样做。另外,data
应该是什么?您的初始状态是一个数组,但您似乎将其用作对象。
是的,所以它应该是一个对象(你使用的是useState([])
)。您可以使用setData(data => ...data, likes: data.likes.filter(...))
之类的内容进行更改。请问,setLiked(() => liked === false)
应该做什么?为什么不setLiked(false)
?
你也直接在这里改变状态:post.likes.push(...)
。我认为最好的方法是使用控制台日志记录或 React DevTools 来调试您的代码,因为如果我无法重现它就很难提供帮助。
【参考方案1】:
正如@iz_ 在 cmets 中指出的那样,您的主要问题是您直接改变状态而不是调用 setState
函数。
为了清楚起见,我将 data
重命名为 post
,因为您说过这是一个表示一篇帖子数据的对象。
const [post, setPost] = useState(initialPost);
您不需要使用liked
作为状态,因为我们已经可以通过查看我们的用户是否在post.likes
数组中来从post
数据中访问此信息。这使我们能够拥有“单一事实来源”,并且我们只需要在一个地方进行更新。
const isLiked = post.likes.some((like) => like.id === user.id);
我对@987654331@ 数组感到困惑。这似乎是一个对象数组,只是 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
更改的帖子属性,然后用编辑的版本覆盖likes
属性。 filter()
和 concat()
都是安全数组函数,它们返回数组的新副本。
我们还需要调用 API 来发布更改。您在“like”和“unlike”场景中使用相同的 url,因此我们可以调用泛化函数 axios.request
并传递方法名称 'post'
或 @987654348,而不是调用 axios.post
和 axios.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>
);
;
CodeSandbox Link
【讨论】:
非常感谢您的回答!我会尝试这种方式:) 嘿@Linda,我试过这个,但我在这一行遇到错误,const isLiked = post.likes.some((like) => like.id === user.id);
,这是错误TypeError: Cannot read property 'likes' of undefined
。你能调查一下吗?正如你所说,likes 数组包含用户的 id 和用户名。
这是我的console.log(post)
看起来像id: 152 likes: [] post_date: "2020-10-25T09:21:14.531431+01:00" postimage_set: (2) […, …] display_pic: "http://127.0.0.1:8000/media/pimage/1/profile_pic_C68UK9J.png" slug: "just-checking-multiple-upload" title: "Just checking multiple upload!" user: "testuser12"
但是当我尝试console.log(post.likes)
时它会抛出这个错误TypeError: Cannot read property 'likes' of undefined
。这是为什么?谢谢
如果我像const isLiked = post && post.likes && post.likes.some((like) => like.id === user.id);
那样更改函数,它不会显示任何错误,并且喜欢和不同的作品,但问题是当我重新加载页面时,即使用户按钮处于不同状态已经喜欢了这篇文章。
Re: TypeError,可能是我们在post
存在之前访问它太早了? post
将是该错误中的 undefined
对象。我假设帖子已经加载,这就是我使用initialPost
的原因。 Idk,但您可以使用可选链接 const isLiked = post?.likes.some((like) => like.id === user?.id);
以上是关于基于api调用的反应js中的按钮不改变状态的主要内容,如果未能解决你的问题,请参考以下文章
当反应原生应用程序启动时,将初始状态从 API 调用传递给 createStore