tag:crieit.net,2005:https://crieit.net/tags/draft.js/feed 「draft.js」の記事 - Crieit Crieitでタグ「draft.js」に投稿された最近の記事 2020-06-07T23:44:07+09:00 https://crieit.net/tags/draft.js/feed tag:crieit.net,2005:PublicArticle/15925 2020-06-07T23:44:07+09:00 2020-06-07T23:44:07+09:00 https://crieit.net/posts/NextJS-5edcfd37d54af NextJSでマークダウンエディタを扱う際の注意点 <h2 id="SSR(サーバサイドレンダリング)の影響"><a href="#SSR%28%E3%82%B5%E3%83%BC%E3%83%90%E3%82%B5%E3%82%A4%E3%83%89%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%29%E3%81%AE%E5%BD%B1%E9%9F%BF">SSR(サーバサイドレンダリング)の影響</a></h2> <p>draft.jsやtui-editorに共通して言えることですが、<br /> 元々ブラウザでの動作を想定して作られているので、<br /> NextJSなどのSSR環境下では<code>window</code>、<code>document</code>などの変数の中身が<code>undefined</code>になり、動作しなくなります。</p> <h2 id="Next.JSではSSRを無効にできる"><a href="#Next.JS%E3%81%A7%E3%81%AFSSR%E3%82%92%E7%84%A1%E5%8A%B9%E3%81%AB%E3%81%A7%E3%81%8D%E3%82%8B">Next.JSではSSRを無効にできる</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr">With no SSR</a></p> <p>インポート方法を変えることでSSRを無効にできるオプションがあります。<br /> dynamic import自体はES2020で導入される構文だとか。</p> <pre><code class="typescript">const DraftEditorNoSSR = dynamic<DraftEditorProp>( () => import('../components/DraftEditor') as any, { ssr: false } ) </code></pre> <h2 id="Draft.jsを断念した理由"><a href="#Draft.js%E3%82%92%E6%96%AD%E5%BF%B5%E3%81%97%E3%81%9F%E7%90%86%E7%94%B1">Draft.jsを断念した理由</a></h2> <p>draft.jsでは再描画した際にカーソルが行の先頭に戻るというバグがあります。<br /> 公式リポジトリでは行の最後に移動する対処も議論されていますが、文章の中で編集したくなったらどうするんだ....という声もちらほら。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/facebook/draft-js/issues/1198">How to stop DraftJS cursor jumping to beginning of text?</a></p> <p>さっさと見切りをつけてToastUI editorに移りました。<br /> そうです、crieitにも採用されているエディタですね。</p> <h2 id="Toast UI Editorで実装できた!"><a href="#Toast+UI+Editor%E3%81%A7%E5%AE%9F%E8%A3%85%E3%81%A7%E3%81%8D%E3%81%9F%21">Toast UI Editorで実装できた!</a></h2> <p>ただし、Toast UI Editorはrefを使わないと値が取れず、<br /> マウントされていない状態でrefがnullになってしまうことにかなり苦しめられました。</p> <p>changeイベントがeditorで発火するのでフラグを更新するようにして、外側でrefを使ってデータを取り出すという実装になりました。<br /> それから、画像アップロードをdisabledにしました。</p> <p>editorコンポーネントを使いまわしたかったので、<br /> 表示用の初期値<code>initialValue</code>と値を更新して外部に渡す<code>setFunc</code>をpropsとして与えています。</p> <pre><code class="javascript">const DraftEditor = ({ initialValue, setFunc }) => { const editorRef = createRef<Editor>(); const [changed, setChanged] = useState(false); useEffect(() => { if (editorRef.current && changed) { setFunc(editorRef.current.getInstance().getMarkdown()); console.log("set"); setChanged(false); } }, [editorRef.current, changed]); return ( <div style=<span>{</span><span>{</span> width: "100vw", <span>}</span><span>}</span> > <div> <Editor previewStyle="vertical" toolbarItems={[ "heading", "bold", "italic", "strike", "divider", "hr", "quote", "ul", "ol", "task", "table", "link", "divider", ]} height="400px" initialEditType="markdown" initialValue={initialValue} ref={editorRef} hideModeSwitch={true} events=<span>{</span><span>{</span> change: (e) => { setChanged(true); }, <span>}</span><span>}</span> /> </div> </div> ); }; export default DraftEditor; export type DraftEditorProp = { initialValue: string; setFunc: Dispatch<any>; }; </code></pre> ckoshien