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

相关推荐
yuki_uix3 分钟前
渲染优化三件套:React.memo、useMemo、useCallback 的使用边界
前端·react.js
labixiong9 分钟前
React Fiber 架构全景解析(一)
前端·react.js
弓.长.1 小时前
ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-fast-image — 高性能图片加载组件
react native·react.js·harmonyos
秋秋小事1 小时前
鸿蒙DevEvo Studio运行React Native生成的bundle文件遇到的一个问题
react native·react.js·鸿蒙
new code Boy1 小时前
Vue3转React速查表
前端·javascript·react.js
早點睡3902 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-linear-gradient-text
javascript·react native·react.js
Easonmax2 小时前
ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-image-picker — 图片选择器
react native·react.js·harmonyos
早點睡3902 小时前
ReactNative项目OpenHarmony三方库集成实战:@react-native-oh-tpl/react-native-fast-image
javascript·react native·react.js
暖阳常伴2 小时前
全栈vue/react+node.js,云服务器windows部署全流程
vue.js·react.js·node.js
大雷神2 小时前
HarmonyOS APP<玩转React>开源教程十二:ModuleCard 模块卡片组件
react.js·开源·harmonyos