You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Slate.js编辑器props变更时重渲染且不触发onChange?

解决Slate编辑器外部内容同步时不触发onChange的问题

你的核心问题是外部note变更时,同步编辑器内容会意外触发onChange导致不必要的笔记更新。可以通过两种思路解决:一是用状态标记区分外部更新和用户编辑,二是临时替换编辑器的onChange方法避免触发回调。

方案一:用Ref标记外部更新

通过一个Ref变量标记当前是否为外部内容同步,在handleEditorChange中跳过这类场景的保存逻辑:

  1. 添加一个Ref用于标记状态
const isExternalUpdate = useRef(false);
  1. 修改useEffect中的内容同步逻辑,使用Slate官方API更新内容并标记状态
import { Transforms } from 'slate';

// ...

useEffect(() => {
  if (!note?.content) return;

  // 标记为外部更新
  isExternalUpdate.current = true;
  
  // 清空现有内容并插入新内容
  Transforms.removeNodes(editor, { at: [], match: () => true });
  Transforms.insertNodes(editor, note.content);
  
  // 重置标记
  isExternalUpdate.current = false;
}, [note, editor]);
  1. 修改handleEditorChange,跳过外部更新场景
const handleEditorChange = (value: Descendant[]) => {
  // 如果是外部更新,直接返回不执行保存
  if (isExternalUpdate.current) return;
  
  note && (note.content = value);
  debouncedUpdate();
};

方案二:临时替换编辑器的onChange方法

在同步外部内容时,临时将编辑器的onChange替换为空函数,完成同步后恢复:

修改useEffect逻辑:

useEffect(() => {
  if (!note?.content) return;

  // 保存原始onChange方法
  const originalOnChange = editor.onChange;
  
  // 临时替换为空函数,避免触发我们的回调
  editor.onChange = () => {};
  
  // 更新编辑器内容
  editor.children = note.content;
  // 触发编辑器内部状态更新
  editor.onChange();
  
  // 恢复原始onChange方法
  editor.onChange = originalOnChange;
}, [note, editor]);

这种方法不需要修改handleEditorChange,直接通过临时屏蔽onChange来避免不必要的保存。

额外优化建议

  • 避免直接修改note.content:note是来自zustand store的props,直接修改属于状态突变,建议创建内容副本后再更新,或者通过store提供的专用方法修改。
  • 初始化逻辑优化:initialValue仅在组件首次挂载时生效,后续note变更依赖useEffect同步是正确的,无需调整。

修改后的完整Editor组件示例

import { Transforms } from 'slate';
import { useState, useEffect, useRef } from 'react';
import { Slate, Editable, withReact } from 'slate-react';
import { createEditor, withHistory } from 'slate-history';
import { useNotesStore } from './your-store-path';

interface EditorProps {
  note: Note | null;
}

function Editor({ note }: EditorProps) {
  const { updateNote } = useNotesStore();
  const [editor] = useState(() => withReact(withHistory(createEditor())));
  const isExternalUpdate = useRef(false);

  const initialContent = note?.content || [
    { type: "paragraph", children: [{ text: "" }] },
  ];

  const debouncedUpdate = debouncedFunction({
    func: () => updateNote(note!),
    delay: 200,
  });

  const handleEditorChange = (value: Descendant[]) => {
    if (isExternalUpdate.current) return;
    // 创建副本避免直接修改props
    if (note) {
      note.content = JSON.parse(JSON.stringify(value));
    }
    debouncedUpdate();
  };

  useEffect(() => {
    if (!note?.content) return;

    isExternalUpdate.current = true;
    Transforms.removeNodes(editor, { at: [], match: () => true });
    Transforms.insertNodes(editor, note.content);
    isExternalUpdate.current = false;
  }, [note, editor]);

  return (
    <section
      id="editor-parent-container"
      className="w-full flex flex-col gap-2 max-w-[100%] h-full py-[30px] px-8 text-light-text dark:text-dark-text overflow-hidden "
    >
      <Slate
        editor={editor}
        initialValue={initialContent}
        onChange={handleEditorChange}
      >
        <SimpleToolbar />
        <Editable
          placeholder="Write something..."
          className="max-w-full w-full h-full outline-none border-none ring-0"
        />
      </Slate>
    </section>
  );
}

export default Editor;

内容的提问来源于stack exchange,提问作者case_sensitive

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:08:25