ReactでMarkdown Editorを爆速で作る
n番煎じだけど気にしない。完成品。
1. create-react-appでひな型を作る
create-react-appを使う。
$ yarn global add create-react-app $ create-react-app react-markdown-editor
待つこと数十秒。yarn
が速いので助かる。
2. Github Pages にデプロイする
create-react-app
のドキュメントによると、Github Pages にデプロイするには、gh-pages
を使うといいらしい。
Github にレポジトリを作ってから、
$ yarn add gh-pages
して、package.json
にデプロイ用のスクリプトを書き加える。
"scripts": { "predeploy": "npm run build", "deploy": "gh-pages -d build", ...
試しに yarn deploy
をしてみると、きちんと Github Pages にデプロイされていた。
作る。
App.js を書き換える。まずはテキストエリアと、テキスト表示を用意する。テキストエリアの値が更新されるたびに state に入れて、テキスト表示場所を更新する。
import React, { Component } from 'react' import autoBind from 'react-autobind' import './App.css' class App extends Component { constructor (props) { super(props) autoBind(this) this.state = { text: '', lines: [] } } render () { let { text, lines } = this.state return ( <div className='App'> <div className='App-header'> Markdown Editor </div> <div className='App-body'> <textarea className='App-box App-textarea' value={text} onChange={this.onChangeText} /> <div className='App-box App-textview'> { lines.map((line, i) => <div className='App-line' key={i}>{line}</div> ) } </div> </div> </div> ) } onChangeText (e) { let text = e.target.value let lines = text.split('\n') this.setState({ text: e.target.value, lines: lines }) } } export default App
とりあえずこれで、(マークダウンではない)テキストエディタができた。
次に、marked
を使って、Markdown 文字列を HTML 文字列に変換する。React で表示する際には、dangerouslySetInnerHTML
を使う。
import React, { Component } from 'react' import autoBind from 'react-autobind' import marked from 'marked' import './App.css' class App extends Component { constructor (props) { super(props) autoBind(this) this.state = { text: '', markedLines: [] } } render () { let { text, markedLines } = this.state return ( <div className='App'> <div className='App-header'> Markdown Editor </div> <div className='App-body'> <textarea className='App-box App-textarea' value={text} onChange={this.onChangeText} /> <div className='App-box App-textview'> { markedLines.map((line, i) => <div key={i} className='App-line' dangerouslySetInnerHTML={line} /> ) } </div> </div> </div> ) } onChangeText (e) { let text = e.target.value let markedLines = text .split('\n') .map((line) => ({ __html: marked(line) })) this.setState({ text, markedLines }) } } export default App
あとはCSSを書いて完成!所要時間は約1時間。
課題
まあ、これはまだ不完全で、一行ずつ HTML に変換しているため、たとえば
1. foo 2. bar 3. baz
なんかがうまく表示されない。
あとは、1文字表示されるたびにテキスト全体を marked()
しているのも効率悪い。