Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
React.js, Draft.jsで作る リッチテキストエディタ開発入門
Search
mottox2
June 21, 2016
Programming
14
14k
React.js, Draft.jsで作る リッチテキストエディタ開発入門
Meguro.es #4 @wantedlyでのトーク内容です
mottox2
June 21, 2016
Tweet
Share
More Decks by mottox2
See All by mottox2
もう一歩進めたい OG画像の動的生成
mottox2
7
1.6k
なぜコピペで使うコンポーネント集を利用するのか?
mottox2
8
6.9k
UIコンポーネントライブラリをうまく使うためにできること / components-with-designer
mottox2
7
3.8k
Figma Plugin公開までの壁を乗り越える
mottox2
2
3k
Puppeteerでつくる画像と動画 / images and videos made with puppeteer
mottox2
0
640
手触りのよいウェブを考える / better-mobile-web
mottox2
3
1.7k
組織と権限とSlack App / slack-app-with-roles
mottox2
1
620
SSRを避けるためにやっていること / ssr-alternative
mottox2
9
3.1k
JSXでつくる宣言的UIなプレゼンテーション / jsx-presentation
mottox2
7
33k
Other Decks in Programming
See All in Programming
『品質』という言葉が嫌いな理由
korimu
0
160
富山発の個人開発サービスで日本中の学校の業務を改善した話
krpk1900
4
370
TokyoR116_BeginnersSession1_環境構築
kotatyamtema
0
110
法律の脱レガシーに学ぶフロントエンド刷新
oguemon
5
730
[JAWS-UG横浜 #80] うわっ…今年のServerless アップデート、少なすぎ…?
maroon1st
1
170
Writing documentation can be fun with plugin system
okuramasafumi
0
120
プログラミング言語学習のススメ / why-do-i-learn-programming-language
yashi8484
0
120
いりゃあせ、PHPカンファレンス名古屋2025 / Welcome to PHP Conference Nagoya 2025
ttskch
1
270
Ruby on cygwin 2025-02
fd0
0
140
個人アプリを2年ぶりにアプデしたから褒めて / I just updated my personal app, praise me!
lovee
0
340
技術を根付かせる / How to make technology take root
kubode
1
240
SwiftUIで単方向アーキテクチャを導入して得られた成果
takuyaosawa
0
260
Featured
See All Featured
How STYLIGHT went responsive
nonsquared
98
5.3k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
132
33k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
Statistics for Hackers
jakevdp
797
220k
Why You Should Never Use an ORM
jnunemaker
PRO
55
9.2k
GraphQLとの向き合い方2022年版
quramy
44
13k
For a Future-Friendly Web
brad_frost
176
9.5k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Making the Leap to Tech Lead
cromwellryan
133
9.1k
GraphQLの誤解/rethinking-graphql
sonatard
68
10k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
Transcript
React.js, Draft.jsͰ࡞Δ ϦονςΩετΤσΟλ։ൃೖ Meguro.es #4 @Wantedly ຊ ༤و (@mottox2)
γΰτͰίίϩΦυϧ ࣗݾհ ຊ ༤و @mottox2 • Wantedly৽ଔҰͷΤϯδχΞ • ϑϩϯτΤϯυ͕͖ •
ʮϑΟʔυʯΛ࡞͍ͬͯ·͢
දݱͷ෯Λ͛ΔͨΊʹ ϒϩάΤσΟλͷϦχϡʔΞϧΛߦͬͨ
γΰτͰίίϩΦυϧ ϦονςΩετΤσΟλ։ൃ ϦονςΩετΤσΟλͷඞཁੑ ΤϯδχΞք۾ͰϚʔΫμϯʴϓϨϏϡʔ͕ྲྀߦ͍ͬͯΔɻ ී௨ͷਓ͔Βͨ͠ΒϦονςΩετΤσΟλ͕ͨΓલɻ ʮฤू͍ͯ͠Δݟͨʹެ։͞ΕΔͷʯͰ͋ͬͯཉ͍ͣ͠ɻ ϦονςΩετΤσΟλ։ൃͷਏ͞ (=contenteditableͱͷઓ͍) ᶃ ϒϥβʹΑͬͯڍಈ͕ҧ͏
ᶄ ཤྺཧ͕େม ᶅ DOMΛ৮Δඞཁ͕͋Δ
γΰτͰίίϩΦυϧ ͜Ε·Ͱ ҎલͷΤσΟλ React+ReduxߏͷதͰɺੜ ͷDOMΛ͍͍ͬͯͨ͡ɻ ֦ுͣ͠Β͍ DOMΛ৮ͬͯཧ͍ͯ͠Δ෦
γΰτͰίίϩΦυϧ Draft.jsͱʁ Facebookͷެ։͍ͯ͠ΔOSSɻ React.js্ͰϦονςΩετΤσΟλΛߏங͢ΔͨΊͷίϯϙʔωϯτΛఏڙ͠ ͯ͘ΕΔɻ https://github.com/facebook/draft-js Ըܙ ᶃ ϒϥβؒͷڍಈͷࠩΛٵऩͯ͘͠ΕΔɻ ᶄ
ΤσΟλશମΛ1ͭͷStateͱͯ͠ཧ͢Δ͔Βɺཤྺཧ؆୯ ᶅ DOMStateͰ͍࣋ͬͯΔͷͰɺJSΦϒδΣΫτͰཧͰ͖Δʢେʣ ߋʹɺReactίϯϙʔωϯτͰϦονͳදࣔͰ͖Δɻ Draft.jsͷ࠾༻
γΰτͰίίϩΦυϧ ී௨ͷinputλά class MyInput extends React.Component { constructor(props) { super(props);
this.state = {value: ''}; this.onChange = (e) => this.setState({value: e.target.value}); } render() { return <input value={this.state.value} onChange={this.onChange} />; } }
γΰτͰίίϩΦυϧ Draft.jsͰཧ͢Δ߹ import React from ‘react’; import {Editor, EditorState} from
'draft-js'; class MyEditor extends React.Component { constructor(props) { super(props); this.state = {editorState: EditorState.createEmpty()}; this.onChange = (editorState) => this.setState({editorState}); } render() { return <Editor editorState={this.state.editorState} onChange={this.onChange} />; } } EditorͷComponent Editorͷঢ়ଶΛද͢State input, textAreaͱಉ͡ײ֮Ͱ͑Δ!͜ΕΛ֦ு͍ͯ͘͠
γΰτͰίίϩΦυϧ Block Block [{ depth: 0, entityRanges: [], inlineStyleRanges: [],
key: "67fie", text: "ݟग़͠", type: "header-two" },{ depth: 0, entityRanges: [], inlineStyleRanges: [], key: "67fie", text: "ී௨ͷςΩετ", type: "unstyled" }] ݟग़͠ Preview ී௨ͷςΩετ typeΛม͑Δ͜ͱͰ ରԠ͢Δཁૉʹม͑Δ͜ͱ ͕Ͱ͖Δ (ex. h1..h6, blockquote, code)
γΰτͰίίϩΦυϧ InlineStyle Block { depth: 0, entityRanges: [], inlineStyleRanges: [
{ length: 2 offset: 0 style: “BOLD” } ], key: "67fie", text: "ଠࣈʹͳΔ", type: "header-two" } InlineStyle ଠࣈʹͳΔ Preview StyleΛม͑Δ͜ͱͰ ରԠ͢ΔελΠϧΛͯΔ ͜ͱ͕Ͱ͖Δɻ (ex. BOLD, ITALIC, UNDERLINE)
γΰτͰίίϩΦυϧ Entity Block { depth: 0, entityRanges: [ { key:
0, length: 3, offset: 0 } ], inlineStyleRanges: [], key: "67fie", text: "ϦϯΫʹͳΔ", type: "unstyled" } entityMap: { 0: { data: { url: “http://wantedly.com“ }, mutability: “MUTABLE”, type: “LINK”, } } Entity Entity ϦϯΫʹͳΔ Preview จࣈɺ০Ҏ֎ͷใ EntityͰཧΛߦ͏ɻ
γΰτͰίίϩΦυϧ DOM͞ΘΓͨ͘ͳ͍ ղܾʂ ͍͍ײ͡ʹ৮Γ͍͢API͕ఏڙ͞Ε͍ͯΔͷͰ ޙBlockInlineStyle, EntityΛ͍͡Δ͚ͩͰྑ͍ɻ
γΰτͰίίϩΦυϧ Custom Block Compoent ReactComponentΛͬͨϦον ͳίϯϙʔωϯτ͕࡞ΕΔɻ Ԡ༻͢ΔͱɺEntityͷσʔλΛߋ ৽ͯ͠ΠϯλϥΫςΟϒͳײ͡ͷ ͷΛ࡞ΕΔɻ data:
{ created_at : "2016-06-21T16:29:55.9 id: 10 provider_name: "Wantedly" thumbnail: “https://d2v9k5u4v94ulw title: "ϞμϯͳڥͰReactΛॻ͖͍ͨ type: “link" updated_at: "2016-06-21T16:29:55.9 url: "https://www.wantedly.com/pro }, mutability: “IMMUTABLE", type: "EMBED" Preview Entity
γΰτͰίίϩΦυϧ HTMLͷม • GithubʹDraft.jsͷσʔλܗ͔ࣜΒHTMLASTʹม͢ ΔϥΠϒϥϦ͕ز͔ͭެ։͞Ε͍ͯΔɻ • WantedlyͰRubyͰDraft.jsͷσʔλܗ͔ࣜΒHTMLʹ ม͢ΔίʔυΛॻ͍ͯରԠ͍ͯ͠Δɻ • iOS͕ωΠςΟϒΞϓϦͰهࣄΛදࣔ͢ΔํࣜΛ࠾༻ͯ͠
͓Γɺ΄΅Draft.jsͷσʔλܗࣜʹἧ͍͑ͯΔɻ
γΰτͰίίϩΦυϧ Draft.jsͷਏ͍ͱ͜Ζ • ຊʹ͋·Γ͍ͬͯΔਓ͕͍ͳ͍ͷͰӳޠΛಡΉ͜ͱʹ ͳΔɻ • ࡉ͔͍ௐΛ͢Δͱ͖ʹdraft.jsຊମͷίʔυಡΉඞཁ͕ ͋Δɻ • ϚϧνόΠτจࣈݻ༗ͷਏ͍ڍಈ͕͋Δɻ
• ݁ہΩϟϨοτͷൣғཧͷਏ͞Δɻ • σϑΥϧτͷվߦͷڍಈ͕ΈͰͳͯ݁͘ߏ͍ͬͨ͡ɻ
γΰτͰίίϩΦυϧ ಋೖͯ͠Έͨײ • Draft.jsΛಋೖ͢Δ͜ͱͰɺ։ൃָ͕ʹͳΔ ͭΒ͍ͱ͜Ζ͋Δ͚Ͳɺࠓ·ͰΑΓ͔ͳΓָɻ ։ൃָ͕ʹͳͬͨɺϢʔβʔ؆୯ʹهࣄΛ࡞ΕΔΑ͏ ʹ͍͖͍ͯͨ͠ɻ
γΰτͰίίϩΦυϧ ࠷ޙʹ
Wantedly FeedͰ ΤϯδχΞϒϩάΛॻ͖·͠ΐ͏ʂ ࠾༻ʹܨ͕ΔʮWantedly͔ͩΒͦ͜ʯͷՁΛఏڙ͠·͢