记React函数式组件外使用全局变量的一个乌龙

背景

最近参与开发了一个模型训练的项目,有一个模型管理的新建页面

此页面的功能有两项

  1. 可以新建模型 + 新建版本

2. 可以基于已有模型新建版本

交互要求

对于第一种情况,新建模型 + 新建版本后,要跳转到新建模型的详情页,展示版本列表;

对于第二种情况,只是新增版本,此时就要跳转到已有模型的详情页,展示版本列表;

以上是基本的交互要求;

实现思路

我们通过页面路由查询字符串中是否有modelId来进行两种情况的区分

针对第一种模式,无 modelId, 用户点击提交,会先去创建 模型,此时后端会返回模型 id,再基于此模型id,创建版本, 并跳转至新创建的模型id 对应的详情页;

第二种,有了 modelId ,会直接创建版本,并在创建完跳转到此modelId对应详情页面;

第一版实现:

代码如下所示, 如按下述代码所示,modelIdTemp 在新建模型后的值没有做清空 ,此时就会出现其它模型在新建版本的时候,在触发页面跳转的时,都没有正确跳转到指定模型的详情页,而是跳转到 modelIdTemp 缓存模型id值的对应得模型详情页,这就是所谓的乌龙也就是 bug 的症结所在。

jsx 复制代码
/**
 * modelId 通过查询字符串传递过来(基于模型创建版本会有)
 * modelParams:创建模型需要的参数
 * taskParams: 创建任务需要的参数
 * navigate: 为 react-reouter-dom v6 中页面路由跳转使用 
 * 
 * 以下代码为简略版
 * 
 */

import {message} from 'antd';
let modelIdTemp;

const ModelAdd = () => {


  /**
  * 保存提交
  */
  const handleSubmit = () => {
    if (modelId) {
      // 基于已有模型 新建 版本
      handleSaveTaskInfo(modelId);
    } else {
      // 新建模型 + 新建版本
      saveModelInfo(modelParams)
        .then((res) => {
          if (res.code === 0) {
            // 后端返回模型Id 保存到 modelIdTemp 中
            modelIdTemp = res.data;
            // 创建任务
            handleSaveTaskInfo(res.data);
          } else {
            message.error(res.message || "模型创建失败");
          }
        })
        .catch((e) => {
          console.log("模型创建失败~", e);
        });
    }
  };

  /**
  * 创建任务
  */
  const handleSaveTaskInfo = (id) => {
    saveTaskInfo(taskParams, id)
      .then((res) => {
        if (res.code === 0) {
          message.success("创建任务成功");
          // 跳转到模型详情页
          navigate(`/model/detail/${modelIdTemp || modelId}`);
        } else {
          message.error(res.message || "创建任务失败");
        }
      })
      .catch((e) => {
        console.log("创建任务失败~", e);
      });
  };

  return <>
    页面内容区域
  </>

}

export default ModelAdd;

解决方法

可以解决的思路有:

  1. 在页面卸载的时候,将 modelIdTemp 重新置为空即可
jsx 复制代码
useEffect(() => {
return modelIdTemp = '';
}, [])
  1. 使用 useRef 来解决
jsx 复制代码
import { useRef } from "react";

import {message} from 'antd';

const ModelAdd = () => {
  let modelIdTemp = useRef();

  /**
  * 保存提交
  */
  const handleSubmit = () => {
    if (modelId) {
      // 基于已有模型 新建 版本
      handleSaveTaskInfo(modelId);
    } else {
      // 新建模型 + 新建版本
      saveModelInfo(modelParams)
        .then((res) => {
          if (res.code === 0) {
            // 后端返回模型Id 保存到 modelIdTemp 中
            modelIdTemp.current = res.data;
            // 创建任务
            handleSaveTaskInfo(res.data);
          } else {
            message.error(res.message || "模型创建失败");
          }
        })
        .catch((e) => {
          console.log("模型创建失败~", e);
        });
    }
  };

  /**
  * 创建任务
  */
  const handleSaveTaskInfo = (id) => {
    saveTaskInfo(taskParams, id)
      .then((res) => {
        if (res.code === 0) {
          message.success("创建任务成功");
          // 跳转到模型详情页
          navigate(`/model/detail/${modelIdTemp.current || modelId}`);
        } else {
          message.error(res.message || "创建任务失败");
        }
      })
      .catch((e) => {
        console.log("创建任务失败~", e);
      });
  };

  return <>
    页面内容区域
  </>

}

export default ModelAdd;

使用建议

如果对于会发生变化的变量,建议 useRef 来 替代全局变量;

相关推荐
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(二)
前端·react.js·前端框架
小白小白从不日白3 小时前
react hooks--useCallback
前端·react.js·前端框架
恩婧3 小时前
React项目中使用发布订阅模式
前端·react.js·前端框架·发布订阅模式
程序员小杨v14 小时前
如何使用 React Compiler – 完整指南
前端·react.js
谢尔登5 小时前
Babel
前端·react.js·node.js
卸任5 小时前
使用高阶组件封装路由拦截逻辑
前端·react.js
清汤饺子8 小时前
实践指南之网页转PDF
前端·javascript·react.js
霸气小男8 小时前
react + antDesign封装图片预览组件(支持多张图片)
前端·react.js
小白小白从不日白8 小时前
react 组件通讯
前端·react.js
小白小白从不日白10 小时前
react hooks--useReducer
前端·javascript·react.js