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

相关推荐
ssshooter14 小时前
看完就懂 useSyncExternalStore
前端·javascript·react.js
青青家的小灰灰1 天前
迈向全栈新时代:SSR/SSG 原理、Next.js 架构与 React Server Components (RSC) 实战
前端·javascript·react.js
青青家的小灰灰1 天前
透视 React 内核:Diff 算法、合成事件与并发特性的深度解析
前端·javascript·react.js
小霖家的混江龙1 天前
从 0 到 1 实现一个 useState
前端·javascript·react.js
晓得迷路了1 天前
栗子前端技术周刊第 118 期 - Oxfmt Beta、Angular GitHub stars、React 基金会...
前端·javascript·react.js
AAA阿giao2 天前
从零构建一个现代登录页:深入解析 Tailwind CSS + Vite + Lucide React 的完整技术栈
前端·css·react.js
昨晚我输给了一辆AE862 天前
为什么现在不推荐使用 React.FC 了?
前端·react.js·typescript
不会敲代码12 天前
深入浅出 React 闭包陷阱:从现象到原理
前端·react.js
不会敲代码12 天前
React性能优化:深入理解useMemo和useCallback
前端·javascript·react.js
不会敲代码12 天前
从入门到进阶:手写React自定义Hooks,让你的组件更简洁
前端·react.js