按钮未根据API调用更改React JS中的状态

时间:2020-10-25 16:53:34

标签: javascript reactjs react-native react-redux jsx

我有此页面,其中显示单个帖子,并且我有一个点赞按钮。如果喜欢该帖子,则当用户单击该按钮时,它将其状态更改为不相似按钮,但是如果不喜欢该帖子,则将注册类似对象,并且将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)
               }
        }
        />)
    }

谢谢,请问是否需要更多详细信息。

1 个答案:

答案 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.postaxios.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>
  );
};

CodeSandbox Link