真实开发踩坑:闭包捕获问题

本文为开发开源项目的真实开发经历,感兴趣的可以来给我的项目点个star,谢谢啦~
具体博文介绍:开源|Documind协同文档(接入deepseek-r1、支持实时聊天)

前置代码

zustand定义
import {create} from "zustand";
import {type Editor} from "@tiptap/react"

interface EditorState{
  editor:Editor | null;
  setEditor:(editor:Editor|null)=>void;
}

export const useEditorStore = create<EditorState>()((set)=>({
  editor:null,
  setEditor:(editor)=>set({editor}),
}))

editor保存设置

这里为editor的生命周期,及editor总会及时保存到zustand

    onCreate({ editor }) {
      setEditor(editor);
    },
    onDestroy() {
      setEditor(null);
    },
    onUpdate({ editor }) {
      setEditor(editor);
    },
    onSelectionUpdate({ editor }) {
      setEditor(editor);
    },
    onTransaction({ editor }) {
      //事务更新时,将编辑器设置到全局状态中
      setEditor(editor);
    },
    onFocus({ editor }) {
      //聚焦时,将编辑器设置到全局状态中
      setEditor(editor);
    },
    onBlur({ editor }) {
      //失去焦点时,将编辑器设置到全局状态中
      setEditor(editor);
    },
    onContentError({ editor }) {
      setEditor(editor);
    },

正文-ImageButton 组件

这里我为tiptap文本编辑器定义了一个图片文件,这里的逻辑就是点击CldUploadButton组件后在本地选择图片,选择图片后上传到cloudinary服务器成功之后触发onSuccess绑定的uploadPhoto事件,其中result?.info?.secure_url就是上传图片的云地址,将此地址传入onChange即可在tiptap文本编辑器中添加图片。

而在使用的过程中遇到了一个问题,下面代码中的console.log(editor)打印结果不一样,第一个打印为正常editor对象,而第二个console.log(editor)打印却是null,这就是由闭包捕获造成的。

为什么zustand总会及时保存editor状态还会打印null?因为editor在初始渲染为null的时候就被onChange()捕获,这个时候onChange()闭包内的editor就被赋值为null我将这里的editor引用取名为editor-1;之后editor的生命周期会及时触发zustand保存最新的editor状态(可参考前置代码),这个时候const { editor } = useEditorStore();中的editor则为最新的editor状态,我给其取名editor-2editor-1是旧引用而editor-2是新引用,由于onChange()是个闭包,所以由于闭包捕获机制,闭包捕获并保存的是旧引用editor-1,而获取不到新引用editor-2,所以闭包内的console.log会打印null

import { useEditorStore } from "@/store/use-editor-store";
import { ImageIcon } from "lucide-react";
import { CldUploadButton } from "next-cloudinary";

export const ImageButton = () => {
  const { editor } = useEditorStore();//来自zustand
  console.log(editor) //打印为最新editor对象
  const onChange = (src: string) => {
    console.log(editor) //打印为null
    editor?.chain().focus().setImage({ src }).run();
  };

  const uploadPhoto = (result: any) => {
    onChange(result?.info?.secure_url);
  };

  return (
    <div className="flex flex-col items-center justify-center">
    //cloudinary云图片上传组件
      <CldUploadButton
        options={{ maxFiles: 1 }}
        onSuccess={uploadPhoto}
        uploadPreset="kdm7bzdm"
      >
        <ImageIcon className="size-4" />
      </CldUploadButton>
    </div>
  );
};

解决方案

直接在闭包函数内获取到当前zustand中的最新editor对象,这样我们就有效解决了闭包捕获问题。

import { useEditorStore } from "@/store/use-editor-store";
import { ImageIcon } from "lucide-react";
import { CldUploadButton } from "next-cloudinary";
import { useCallback } from "react";

export const ImageButton = () => {
  const onChange = (src: string) => {
    console.log(editor);//打印为最新editor对象
    const currentEditor = useEditorStore.getState().editor; 
    currentEditor?.chain().focus().setImage({ src }).run();
  }

  const uploadPhoto = (result: any) => {
    onChange(result?.info?.secure_url);
  };

  return (
    <div className="flex flex-col items-center justify-center">
      {/* 图片插入下拉菜单 */}
      <CldUploadButton
        options={{ maxFiles: 1 }}
        onSuccess={uploadPhoto}
        uploadPreset="kdm7bzdm"
      >
        <ImageIcon className="size-4" />
      </CldUploadButton>
    </div>
  );
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mebius1916

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值