You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在React中集成CodeMirror 6:如何替换编辑器完整内容?

解决CodeMirror 6与React外部State同步的问题

你的问题核心是:当外部操作更新React state中的code值时,CodeMirror 6编辑器没有同步更新内容——因为当前代码只在编辑器初始化时加载了初始值,后续的state变化没有通知到编辑器实例。

下面是具体的修复方案,我会一步步说明:

关键思路

CodeMirror 6的内容是通过EditorState管理的,要动态更新内容,需要通过view.dispatch方法发送状态更新。同时要避免循环更新:编辑器内修改会触发setCode,外部修改又触发编辑器更新,所以需要判断内容是否真的变化再执行更新。

修改后的完整代码

import React, { useState, useRef, useEffect } from 'react';
import { basicSetup, EditorView } from 'codemirror';
import { EditorState } from '@codemirror/state';
import { keymap } from '@codemirror/view';
import { defaultKeymap } from '@codemirror/commands';

const CodeEditor: React.FC = () => {
  const [code, setCode] = useState("initial code state that can be changed externally");
  const editorRef = useRef<HTMLDivElement>(null);
  const viewRef = useRef<EditorView | null>(null);

  // 初始化编辑器
  useEffect(() => {
    if (!editorRef.current) return;

    // 监听编辑器内容变化,同步到React state
    const onUpdate = EditorView.updateListener.of((update) => {
      if (update.docChanged) {
        const newCode = update.state.doc.toString();
        // 避免循环更新:只有当编辑器内容和state不一致时才更新state
        if (newCode !== code) {
          setCode(newCode);
        }
      }
    });

    const startState = EditorState.create({
      doc: code,
      extensions: [
        basicSetup,
        keymap.of(defaultKeymap),
        onUpdate,
        EditorState.tabSize.of(2),
        EditorState.indentUnit.of(2),
        // 把原来的配置移到对应的扩展里
        EditorView.lineNumbers(),
        EditorView.lineWrapping,
        EditorView.styleActiveLine(),
        EditorView.viewportMargin.of(99),
        EditorView.cursorScrollMargin.of(48),
      ],
    });

    const view = new EditorView({
      state: startState,
      parent: editorRef.current,
    });

    viewRef.current = view;

    return () => {
      view.destroy();
      viewRef.current = null;
    };
  }, []);

  // 监听外部state变化,同步到编辑器
  useEffect(() => {
    if (!viewRef.current) return;

    const currentDoc = viewRef.current.state.doc.toString();
    // 避免循环更新:只有当state内容和编辑器不一致时才更新编辑器
    if (code !== currentDoc) {
      viewRef.current.dispatch({
        changes: {
          from: 0,
          to: currentDoc.length,
          insert: code,
        },
      });
    }
  }, [code]);

  return <div ref={editorRef} style={{ width: '300px' }} />;
};

export default CodeEditor;

核心修改点说明

  • 新增viewRef保存编辑器实例,方便后续调用dispatch更新内容
  • 拆分两个useEffect:一个负责初始化编辑器,另一个专门监听code的外部变化
  • 在两个方向的同步中都添加了内容一致性判断,彻底避免循环更新
  • 把原来的配置项(如tabSizelineNumbers)迁移到CodeMirror对应的扩展API中,更符合CM6的设计规范

这样修改后,无论是用户在编辑器内修改内容,还是外部操作更新code state,两者都能保持同步啦。

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

火山引擎 最新活动