我正在开发一个网络应用程序,用户可以在其中报名选修科目,每个选修科目的可用名额有限.我遇到了一个问题,即使只有一个可用的插槽,多个用户也可以同时注册.

const handleApprove = async () => {
  if (isEnrolled || isConfirming) {
    return; 
  }

  setIsConfirming(true);

  const shouldEnroll = window.confirm("Are you sure you want to enroll?");

  setIsConfirming(false);

  if (shouldEnroll) {
    try {
      const firestore = firebase.firestore();
      const electiveRef = firestore.collection("electives").doc(project.id);
      const userRef = firestore.collection("users").doc(user.uid);

      await firestore.runTransaction(async (transaction) => {
        const electiveDoc = await transaction.get(electiveRef);
        if (!electiveDoc.exists) {
          throw new Error("Elective document does not exist!");
        }

        const electiveData = electiveDoc.data();
        const updatedSlots = electiveData.slots - 1;

        if (updatedSlots >= 0) {
         
          const updatedElectiveDoc = await transaction.get(electiveRef);
          const updatedElectiveData = updatedElectiveDoc.data();
          const remainingSlots = updatedElectiveData.slots;

          if (remainingSlots >= 0) {
            
            transaction.update(electiveRef, { slots: updatedSlots });

  
            await userRef.update({
              isEnroll: true,
              elective: project.name,
              subjectCode: project.details,
            });
            
            logout();
            setIsEnrolled(true);
          } else {
            alert("No available slots for enrollment in this elective.");
            setIsEnrolled(false); // Reset isEnrolled if enrollment fails
          }
        } else {
          alert("No available slots for enrollment.");
          setIsEnrolled(false); // Reset isEnrolled if there are no available slots
        }
      });
    } catch (error) {
      console.error("Error enrolling:", error);
      alert("An error occurred while enrolling. Please try again.");
      setIsEnrolled(false); // Reset isEnrolled if enrollment fails
    }
  }
};

当只有一个可用插槽时,多个用户仍然可以注册,即使没有插槽,用户也可以注册. 我需要确保:

  1. 当只有一个可用插槽时,也只有一个用户可以注册,即使他们完全同时注册.
  2. 当没有可用插槽时,用户将无法注册.

我预计,通过使用Firerestore事务,当只有一个可用插槽时,只有一个用户能够注册,而当没有可用插槽时,用户将被阻止注册.我判断了在线解决方案,每条帖子都告诉我要实现交易.

推荐答案

我建议在交易中少做一些事情.限制其仅处理注册逻辑.

您可以传递return value from the transaction以了解其状态,并在结果后执行所有失败/成功的操作以在外部注册逻辑.

此外,请使用事务更新用户文档,以便在事务失败/再试时回滚更新的字段.

try 这样的东西

const didEnroll = await firestore.runTransaction(async (transaction) => {
        const electiveDoc = await transaction.get(electiveRef);
        if (!electiveDoc.exists) {
          throw new Error("Elective document does not exist!");
        }

        const electiveData = electiveDoc.data();
        if (electiveData.slots == 0) {
          // Do all the other failed to enroll actions after returning this
          return false;
        }
        
        // Only do things if there are available slots. 
        // Let the transaction just end with no change if nothing to be done
        transaction.update(electiveRef, {            
          slots: firestore.FieldValue.increment(-1), // Decrement the counter directly
        });

        // Update with transaction so its values are rolling back on retry
        await transaction.update(userRef, {
          isEnroll: true,
          elective: project.name,
          subjectCode: project.details,
        });

        return true; 
      });

Reactjs相关问答推荐

更新数据时有关CQR和更新UI的问题

在一个blog函数中调用setState钩子

react -在选项中 Select 我想要显示和图像.但当我 Select 该选项时,我希望控件仅显示该选项的文本部分

React组件未出现

无法使用 Suspense 和 Await 获取数据

无法通过 fetch 获取数据到上下文中

如何在 ChartJS 中创建此图表?在 NEXTjs 和 TSX 中

无法通过react 路由中的链接传递信息

使用登录保护 React 中的 SPA 应用程序 - 为什么这种方法不起作用?

顶点图表甜甜圈项目边框半径

在 React 中将路径与查询变量匹配

react-markdown 不渲染 Markdown

谁能根据经验提供有关 useEffect 挂钩的更多信息

如何制作基于对象 struct 呈现数据的可重用组件?

在 Remix 中使用 chartjs 和 react-chartjs-2 会出现错误react-chartjs-2未在您的 node_modules 中找到

react 页面更新

如何在自定义 JSX 元素上声明属性?

Lodash 在命名导入中导入整个包

npm init vite@latest 和 npm init vite 有什么区别?

在状态中存储多个选定的选项