本文为开发开源项目的真实开发经历,感兴趣的可以来给我的项目点个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-2
。editor-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>
);
};