记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 来 替代全局变量;

相关推荐
每天都有好果汁吃13 分钟前
基于 react-use 的 useIdle:业务场景下的用户空闲检测解决方案
前端·javascript·react.js
天天扭码1 小时前
面试必备 | React项目的一些优化方案(持续更新......)
前端·react.js·面试
萌萌哒草头将军2 小时前
🏖️ TanStack Router:搜索参数即状态!🚀🚀🚀
javascript·vue.js·react.js
咔咔库奇3 小时前
开发者体验提升:打造高效愉悦的开发环境
前端·javascript·vue.js·react.js·前端框架
红衣信4 小时前
从原生 JS 到 Vue 和 React:前端开发的进化之路
前端·vue.js·react.js
yanhhhhhh5 小时前
React中的hydrateRoot
react.js
萌萌哒草头将军8 小时前
🏖️ 舒服,原来写代码还可以这么享受😎!沉浸式敲代码神器!
javascript·vue.js·react.js
工呈士8 小时前
React 性能监控与错误上报
前端·react.js·面试
itslife9 小时前
构建 react - jsx
前端·react.js
市民中心的蟋蟀9 小时前
第十章 案例 4 - React Tracked 【下】
javascript·react.js·架构