挨拶
55代プログラミング研究会 わせりんです。Web系イキれたら楽しいだろうなと思っています。
Next.jsとEditor.jsでブロックエディタを作る時に難しかったことが色々あったので、後継の役に立てばと思ってこの記事を生成しました。
対象者
Notionに惚れ込んでたり、HackMDが大好きな人 兼 自作してみたいという人。
この記事を読めば多分1時間くらいでそれっぽいエディタがnext.jsで作れるようになります。
完成図
こんな感じのイケてるエディタが出来上がる予定です。かっこいいですね。
公式のドキュメントはこちら。英語読めちゃうよって人はこっちのほうが早いです。 Editor.js
お断り
今回はwindows10, node version 14.18.1です。それ以外のバージョンはうまくいかないかもしれません。環境によっては動かない可能性があります。
また、解説の内容は一部厳密性を欠いていることがあります。ご留意ください。
Next.jsとは?
この記事を読む方はおそらく知っていますが、少しだけ解説します。
webアプリを作るフレームワークの一つで、Reactの拡張的な存在です。サーバー機能を持っていることもあり、フロント側だけで気楽にwebアプリが作れることもあります。
ポケモン最新作のホームページはNext.jsを使っていたりします。
Next.jsの特徴は主に3つです。 1. 初期データを埋め込んだHTMLをブラウザに送信するので、無駄にJSファイルを実行せずにすみます。つまり、読み込み時間が早いです。 2. ファイルの配置によってURLルーティングができます。 3. 設定がある程度自動でやってくれます。 つまり、描画が早くて開発が簡単になります。とっても便利ですね。 しかし、Next.jsのサーバーサイドで構築するという機能には問題もあります。これが今回のメインポイントなので後ほど解説します。
1について細かく知りたい人はこのサイトがとても良いでしょう。 Server Side Renderingについて知るべきこと。Server Side Renderingとは何か? それによって何が改善されるのか?(前編) ng-japan 2017 - Publickey
Editor.jsとは?
Notionっぽいブロックエディタを簡単に使えるJavaScriptのライブラリです。 出力がJSON形式で汎用なデータが取れます。 カスタマイズ性が高く、自分好みのエディタが使えます。 大まかなイメージは完成図を参照してみてください。
詳細はこちらから。
シンプルで使ってみたいWYSIWYGエディター Editor.js | アールエフェクト
作り方
next jsの環境構築
コマンドラインツールから任意フォルダ上で
create-next-appコマンドを実行するとnext.jsのプロジェクトフォルダが作成されます。
※next-appには任意のプロジェクト名を設定します。
npx create-next-app next-app
作成したフォルダ内で npm run dev コマンドを実行するとローカル上でアプリが起動します。
npm run dev
これで、このページが確認できれば一旦完成です。
詳細はこちらで確認してください。
editor jsの環境構築
Editor.jsをインストールします。
npm i @editorjs/editorjs — save
これでひとまずEditor.jsは使えます。 次に初期設定をします。 Editorの設定を行うファイルとEditorを表示するindex.jsファイルに分けて作ります。使うファイルの構成は以下のとおりです。
/components - Editor.js - tool.js /pages - index.js
next.jsがメインで表示するIndex.jsは以下のコードで実装します。 ここでは主に、Next.jsの特徴であるSSRを無効にしつつ、Editorを埋め込んで居ます。
import dynamic from "next/dynamic"; const CustomEditor = dynamic(() => import("../components/Editor.js"), { ssr: false, }); const Home = () => { const [data, setData] = useState(); return ( <> <h1>Editor</h1> <CustomEditor data={data} onChange={setData} holder="editorjs-container" /> </> ); }; export default Home;
このコードでは、ブロックエディタ画面をサーバーサイドでのレンダリング後に読みこむためにdynamicメソッドを利用しています。ssrは falseにしないとバグります。
Indexで表示するエディター画面は、下記のコードで作ります。 ここでは、hooksをつかって変更内容を保存し、index.jsにわたす内容を作成しています。
import React, { memo, useEffect, useRef } from "react"; import EditorJS from "@editorjs/editorjs"; import TOOLS from "./tools"; const CustomEditor = ({ data, onChange, holder }) => { //initialize editorjs const ref = useRef(); useEffect(() => { //initialize editor if we don't have a reference if (!ref.current) { const editor = new EditorJS({ holder: holder, tools: TOOLS, autofocus: true, data, placeholder: "Start writing your story...", async onChange(api) { const data = await api.saver.save(); onChange(data); }, }); ref.current = editor; } //add a return function handle cleanup return () => { if (ref.current && ref.current.destroy) { ref.current.destroy(); } }; }, []); return ( <> <div id={holder}></div> </> ); }; // 状態の変更をこのFCの変更によってのみ検知する。 export default memo(CustomEditor);
const ref = useRef();:
useRefを使用して初期化されます。このhookは、カスタムエディターの参照を保持するために使用されます。
useEffect(() => {...}, []);:
この行では、 useEffectを使用して、カスタムエディターを初期化するための関数を定義します。初回のレンダリング時のみ実行しています。
!ref.currentで refの中身が無い時にインスタンス生成するようにしています。
const editor = new EditorJS({...});: この行では、 EditorJSクラスの新しいインスタンスを作成します。このインスタンスには、次の引数が渡されます。
holder: この引数は、カスタムエディターが表示されるHTML要素のIDを指定します。
tools: この引数は、EditorJSに対応するカスタムツールを定義したJavaScriptファイルを指定します。 autofocus: この引数は、エディターを自動的にフォーカスするかどうかを指定します。
data: この引数は、エディターに表示する初期データを指定します。
placeholder: この引数は、エディターが空の場合に表示するプレースホルダー文字列を指定します。 async onChange(api) {...}: この引数は、エディターの内容が変更されたときに呼び出される非同期関数を指定します。この関数では、 api.saver.saveメソッドを使用して、エディターの内容を保存し、 onChange関数を呼び出して、変更を外部に通知します。
return () => { if (ref.current && ref.current.destroy) { ref.current.destroy(); }
ここでは、すでに定義されているときに、2つ以上エディターを作らないようにしています。
最後に、
<> <div id={holder}></div> </>
この部分がindex.jsで表示されるようになります。
Editorのプラグインをtools.jsにまとめて記載しています。これらのツールについては、全て
npm i --save @editorjs/プラグイン名
でインストールしてから下記のように記載してみてください。
tool.js
import Header from "@editorjs/header"; import CheckList from "@editorjs/checklist"; import code from "@editorjs/code"; import Delimiter from "@editorjs/delimiter"; import Embed from "@editorjs/embed"; import InlineCode from "@editorjs/inline-code"; import LinkTool from "@editorjs/link"; import List from "@editorjs/list"; import Quote from "@editorjs/quote"; import { Paragraph } from "@editorjs/paragraph"; const TOOLS = { header: { class: Header, }, }, checklist: CheckList, code: code, delimiter: Delimiter, embed: Embed, inlineCode: InlineCode, linkTool: LinkTool, list: List, quote: Quote, }; export default TOOLS;
toolsについては、書き方が固定されているので、このまま使ってください。基本的には、importしたプラグインの内容をTOOLS以下で呼び出しています。
Ultimate Guide on how to setup Editor.js with Next.js 12+ (Typescript) | ReactHustle
Next.js & Editor.js Complete Setup Guide | by Fazley Rabbi | Medium
注意点
- ssrをfalseにすること
- 一回初期化したら二回目以降を止める(useRefで状態が出来上がっているかどうかを検知) この点抜けていると、バグがおきるので気をつけてください。
拡張方法
前述したとおり、プラグインをtools.jsに追加すれば出来ます。
また、自分でプラグインを作成することも可能です。ここでは細かく言及しませんが、documentにて詳細な情報を載せています。その他には、このサイトを参照してみてください。
Editor.jsを使ってテキストエディタを作る - Qiita
まとめ
ここまでで、ブロックエディターをNext.js環境下で作成する方法をまとめました。色々欠けている点はありますが、所々で添付した参考サイトと合わせれば、自分だけの完璧なエディターを作れるようになります。
皆様のイチオシエディターを待っています
参考文献
神ドキュメント
次回予告
明日は56代のなださんによる、「初心者向け顔の描き方講座」です。お楽しみに!