以下是我測試的環境 node:v15.12.0 幾個套件的版本: json draft-js: ^0.11.7, next: 11.1.2, react: 17.0.2 最近在嘗試為NextJS 加入一個rich text editor,或者可會叫WYSIWYG (what you see is what you get),中文有人稱之為「即見即所得」的網頁上給用戶發表內容時所用的編輯器,正如wordpress、討論區等等都會用到 在ReactJS 及NextJS 中較多人推薦的有DraftJS , Quill, Slate 這3個,原來早已不是ckeditor和tinymce之爭了……

DraftJS vs Quill vs Slate

DraftJS

3個以下載量而論slate被我放棄了,因為網上找了很多討論,相對比較少,因為下載量=使用人數=遇到各類問題的機會,我一直深信當我遇到這問題時,世上另一角落都有人遇上,所以一套package足夠多人用,當我遇上問題時就較早解決,所以我放棄了slate

然後是quill vs draftJS,我選擇了draftJS

原因一 :draftJS update比quill 較密

原因二:draftJS 是facebook出的

原因三:當時學react-native 時被編輯器所困,當時發現很多人也是推薦用draftJS,所以為了react-native跟web app 用戶能有更近接的體驗

原因四:draftJS比quill 更「裸」 quill 官方在demo的文件中還有些toolbar 給你,但draftJS更少,draftJS給我的感覺就是個textarea,反正一定有超多東西要搞的,不如由0開始?! 所以最終選擇了draftJS

題外話補充:有很多人推薦用react-draft-wysiwyg,是其他人將draftJS整理得沒有那麼「裸」,功能較完整的版本,但我發現更新不夠密集,以及對nextJS支援也會有些少問題,所以放棄沒有用,因為原生的draftJS是有插件包的,當然如果你不是用NextJS單純用ReactJS建議你可以去了解一下這個懶人包draftJS

draftJS 在 ReactJS 上運作還是遇上的問題較少,但在NextJS 上則遇上更多問題,主要卡在以下

1. SSR問題,選擇了NextJS不就是為了SSR嗎?但draftJS會因為SSR而發生錯誤!

比如官方文檔是教你

const [editorState, setEditorState] =useState(()=>EditorState.createEmpty());

但這樣會報錯的官方的 issue#1199
不能用createEmpty(), 而是建立一個 emptyContentState 並用另一個functioncreateWithContent()來解決 你大致應該這樣寫

const [editorState, setEditorState] = useState(EditorState.createWithContent(emptyContentState));

const emptyContentState = convertFromRaw({
  entityMap: {},
  blocks: [
    {
      text: '',
      key: 'foo',
      type: 'unstyled',
      entityRanges: [],
    },
  ],
});

2. 當解決了上面的問題,你終於可以在一個空白的Textarea 上打字而不收到錯誤提示了!但你在console.log 中會看到另一個報錯

Warning: Prop `data-editor` did not match. Server: 6ap7k Client: dooth

後來發現是 data-editor 這個prop的問題,原來這問題在2016年時已經有人提了官方不如修正此問題來方便SSR #796 最終在很多不同的issues中被我找到了較多人提及的一點 #385 原來又是SSR問題?!
簡言之,就是類似isLoading 常見的做法,做給draftJS就ok了

const [editor, setEditor] = useState(false);
  useEffect(() => {
    setEditor(true);
  }, [editorState]);
  return (
      <div className=App>
        {editor ? (
          <Editor />
        ) : null}
      </div>
)

3. 官方教學也不是functional component?這個我也不知是否算問題?

NextJS 官方demo或者一些教學都已全面是functional component 以及用 React Hooks 來做了,但 draftJS 雖然有大量的demo 或是都是 class component,對於近年剛剛接觸React的人,是完全依賴React Hooks,所以在初期試用demo時會經常改漏了this.xxx

4. 全部代碼

(注:這只是一個純空白頁面,只當DraftJS +NextJS init setting)

import { Editor, EditorState, convertFromRaw } from 'draft-js';
import { useEffect, useState } from 'react';
function App() {
  const [editorState, setEditorState] = useState(
    EditorState.createWithContent(emptyContentState)
  );
  const [editor, seteditor] = useState(false);
  console.log();
  useEffect(() => {
    seteditor(true);
  }, [editorState]);
  return (
    <div>
      <div className=App>
        {editor ? (
          <Editor editorState={editorState} onChange={setEditorState} />
        ) : null}
      </div>
    </div>
  );
}
const emptyContentState = convertFromRaw({
  entityMap: {},
  blocks: [
    {
      text: '',
      key: 'foo',
      type: 'unstyled',
      entityRanges: [],
    },
  ],
});
export default App;