以下是我測試的環境

node:v15.12.0

幾個套件的版本:

"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 vs Quill vs Slate

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`

 

全部代碼

(注:這只是一個純空白頁面,只當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;