如何克服 React hook useState 异步行为?

How to overcome the React hook useState async behavior?

我在 useState 异步行为方面遇到了一些问题,这也可能与我是新手的 Redux 相关。

import React, { useState, useEffect } from "react"
import { useSelector, useDispatch } from "react-redux"
import { Link } from "react-router-dom"
import {
  getAlltopics,
  joinAtopic,
  leaveAtopic,
} from "../../../redux/actions/topicActions"
import Icon from "../Icon"

const TopicCard = ({ topics }) => {
  const user = useSelector((state) => state.user)
  const [join, setJoin] = useState(false)
  const dispatch = useDispatch()
  console.log(join)

  const leaveTopicHandler = async () => {
    setJoin(false)
    dispatch(leaveAtopic(topics._id))
  }

  const JoinTopicHandler = () => {
    setJoin(true)
    dispatch(joinAtopic(topics._id))
  }

  useEffect(() => {
    const checkJoinedUser = () => {
      topics.members.map((member) => {
        if (member._id === user?._id) setJoin(true)
      })
    }
    checkJoinedUser()
    dispatch(getAlltopics())
  }, [join, dispatch])
return (
    <div
      key={topics._id}
      className="flex flex-col justify-between w-48 h-72 bg-white shadow-xl rounded-br-3xl rounded-bl-3xl rounded-tr-3xl"
    >
      <Link to={`/topics/${topics._id}`}>
        <section className="">
          <img
            src={topics.bannerImage}
            alt="topic_Image"
            className="object-cover h-48 w-full rounded-tr-3xl"
          />
        </section>
        <section className="border-b-2 border-grey-light ml-3 mr-3 h-12 flex items-center">
          <h1 className="text-lg">{topics.title}</h1>
        </section>
      </Link>
      <section>
        <div className="flex justify-between">
          <div className="flex p-3">
            <Icon iconName="member" iconStyle="fill-inactive text-grey-dark" />
            <span>{topics.members?.length}</span>
            <Icon iconName="file" iconStyle="fill-inactive text-grey-dark" />
            <span>{topics.recources?.length}</span>
          </div>
          <div className="p-3">
            {join ? (
              <button type="button" onClick={leaveTopicHandler}>
                <Icon
                  iconName="follow"
                  iconStyle="fill-active text-grey-dark"
                />
              </button>
            ) : (
              <button type="button" onClick={JoinTopicHandler}>
                <Icon
                  iconName="follow"
                  iconStyle="fill-inactive text-grey-dark"
                />
              </button>
            )}
          </div>
        </div>
      </section>
    </div>
  )
}

我已经定义了一个 join 变量来处理一个按钮,根据它是 true 还是 false 将显示或不显示某些方面,此外,如果它是 false 用户可以加入主题,如果它为真,用户可以离开主题,因为它在函数 JoinTopicHandlerleaveTopicHandler 中很明显。在加入主题之前,它看起来像这样:before joining a topic, as it is possible to see, the join variable it's set to false, because I am not in the topic. When joining the topic, after joining the topic, the joinvariable is set to true, the button changed, although the user count didn't changed for 2 (sometimes it does, sometimes I have to refresh the page for it to render), but the weirdest thing is when leaving the topic, as it's shown in the console,leaving the topic join 变量变为 false 但随后它自己再次变为 true 并且按钮看起来仍然相同,我无法修复这个......

不知道 leaveAtopic 函数到底做了什么,我的猜测是因为 join 在你的 useEffect 挂钩依赖项中,点击离开按钮会发生什么:

  • leaveTopicHandler 是 运行
  • setJoin(false) 导致重新渲染,因为 join 是 useEffect 的依赖项,我们再次 运行 它
  • 调度leaveAtopic开始,但我假设那里有异步逻辑
  • topics 还没有改变,所以当 useEffect 运行s topics.members 仍然包含用户

对于 join,您可能甚至不需要 useState/useEffect,而是可以执行以下操作:

const join = !!topics.members.find((member) => member._id === user?._id))

所以我从我的应用程序后端开始有一些错误,首先,我没有返回更新的主题,因为我没有正确设置调用。

const addUserToTopicDb = async (topicId, user) => {
  try {
    return await Topic.findByIdAndUpdate(topicId, {
      $push: { members: user },
    }, {new: true});
  } catch (error) {
    throw new Error(error);
  }
};

我必须添加 {new: true} 才能获得更新的主题。这是一个重大错误。其次,我在 reducer 上的代码无法正常工作,因为我是新手,并且通过解决此类问题来学习。

我将 reducer 代码更改为:

case JOIN_TOPIC:
      return {
        allTopics: state.allTopics.map((eachTopic) =>
          eachTopic._id === action.payload.topic._id
            ? action.payload.topic
            : eachTopic
        ),
      }
    case LEAVE_TOPIC:
      return {
        allTopics: state.allTopics.map((eachTopic) =>
          eachTopic._id === action.payload.topic._id
            ? action.payload.topic
            : eachTopic
        ),
      }

这基本上意味着,如果我有一个具有相同 _id 的主题,请将其替换为我一开始没有返回的新更新主题。

在此之后,一切开始顺利进行。很遗憾我认为问题是由于 useState... 当它一直在我的数据库设置和 reducer 中...