fix(write): 编辑模式回显空白由 effect 竞态导致

编辑文章时编辑器空白。根因:edit_post 由 None→Some 时,回填 effect
(写 content signal)与初始化 effect(读 content signal)在同一 tick
触发,无保证先后顺序。初始化 effect 经常先跑,读到 content()= 跳过
setMarkdown,且 editor_content_set=true 永久阻止重试。

修复:初始化 effect 直接从 edit_post(真值源)取 content_md 回填,不再
依赖 content signal——后者是派生值,与回填 effect 存在竞态。两个 effect
都已在 edit_post 上订阅,回填时读取它没有竞态。
This commit is contained in:
xfy 2026-07-01 15:41:05 +08:00
parent a4331bd813
commit 7de97d8222

View File

@ -154,7 +154,11 @@ fn write_editor(post_id: Option<i32>) -> Element {
// use_effect 在首次渲染后跑,此时 #tiptap-editor 容器已挂载。
#[cfg(target_arch = "wasm32")]
use_effect(move || {
// 编辑模式:等数据加载完再初始化(避免空内容覆盖回填)
// 编辑模式:等数据加载完再初始化(避免空内容覆盖回填)。
// 回填内容直接从 edit_post唯一可信源不走 content signal——
// content 由独立的回填 effect 写入,两者在 edit_post 变更同一 tick 触发时
// 没有保证的先后顺序:本 effect 可能先于回填 effect 跑,读到仍是空串,
// 导致 setMarkdown 被跳过、编辑器空白(且 editor_content_set=true 阻止重试)。
if is_edit && edit_post().is_none() {
return;
}
@ -193,11 +197,16 @@ fn write_editor(post_id: Option<i32>) -> Element {
// —— create同步返回找不到容器返回 None构造失败抛异常——
match crate::tiptap_bridge::get_module().create("tiptap-editor", &opts) {
Ok(Some(inst)) => {
// 编辑模式回填create 成功立即回填(实例已创建,时机确定)
// 编辑模式回填create 成功立即回填(实例已创建,时机确定)。
// 直接从 edit_post 取 content_md本 effect 已在上面 guard 了 edit_post.is_none()
// 而 edit_post 是异步加载后一次性写入、之后只读的真值源,读取它没有竞态。
// 用 editor_content_set 防重复回填effect 重跑时跳过)。
if is_edit && !editor_content_set() {
let md = content();
if !md.is_empty() {
inst.set_markdown(&md);
if let Some(post) = edit_post() {
let md = &post.content_md;
if !md.is_empty() {
inst.set_markdown(md);
}
}
editor_content_set.set(true);
}