记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 小时前
React Native 全栈开发实战班 - 数据管理与状态之Zustand应用
javascript·react native·react.js
老码沉思录3 小时前
React Native 全栈开发实战班 :数据管理与状态之React Hooks 基础
javascript·react native·react.js
我认不到你3 小时前
antd proFromSelect 懒加载+模糊查询
前端·javascript·react.js·typescript
凹凸曼打不赢小怪兽6 小时前
react 受控组件和非受控组件
前端·javascript·react.js
鑫宝Code6 小时前
【React】状态管理之Redux
前端·react.js·前端框架
2401_8576100311 小时前
深入探索React合成事件(SyntheticEvent):跨浏览器的事件处理利器
前端·javascript·react.js
fighting ~12 小时前
react17安装html-react-parser运行报错记录
javascript·react.js·html
老码沉思录12 小时前
React Native 全栈开发实战班 - 列表与滚动视图
javascript·react native·react.js
老码沉思录13 小时前
React Native 全栈开发实战班 - 状态管理入门(Context API)
javascript·react native·react.js
老码沉思录16 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js