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
15k
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.9k
なぜコピペで使うコンポーネント集を利用するのか?
mottox2
8
7.2k
UIコンポーネントライブラリをうまく使うためにできること / components-with-designer
mottox2
7
3.9k
Figma Plugin公開までの壁を乗り越える
mottox2
3
3.5k
Puppeteerでつくる画像と動画 / images and videos made with puppeteer
mottox2
0
700
手触りのよいウェブを考える / better-mobile-web
mottox2
3
1.8k
組織と権限とSlack App / slack-app-with-roles
mottox2
1
670
SSRを避けるためにやっていること / ssr-alternative
mottox2
9
3.2k
JSXでつくる宣言的UIなプレゼンテーション / jsx-presentation
mottox2
7
33k
Other Decks in Programming
See All in Programming
AWS発のAIエディタKiroを使ってみた
iriikeita
1
180
Design Foundational Data Engineering Observability
sucitw
3
190
AI時代のUIはどこへ行く?
yusukebe
16
8.7k
基礎から学ぶ大画面対応(Learning Large-Screen Support from the Ground Up)
tomoya0x00
0
400
モバイルアプリからWebへの横展開を加速した話_Claude_Code_実践術.pdf
kazuyasakamoto
0
310
奥深くて厄介な「改行」と仲良くなる20分
oguemon
1
510
Namespace and Its Future
tagomoris
6
700
Deep Dive into Kotlin Flow
jmatsu
1
290
AIと私たちの学習の変化を考える - Claude Codeの学習モードを例に
azukiazusa1
7
3.3k
MCPで実現するAIエージェント駆動のNext.jsアプリデバッグ手法
nyatinte
7
1.1k
機能追加とリーダー業務の類似性
rinchoku
2
1.2k
2025 年のコーディングエージェントの現在地とエンジニアの仕事の変化について
azukiazusa1
21
11k
Featured
See All Featured
Building a Modern Day E-commerce SEO Strategy
aleyda
43
7.6k
A designer walks into a library…
pauljervisheath
207
24k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
How to Ace a Technical Interview
jacobian
279
23k
Java REST API Framework Comparison - PWX 2021
mraible
33
8.8k
Scaling GitHub
holman
463
140k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
61k
Build The Right Thing And Hit Your Dates
maggiecrowley
37
2.8k
Statistics for Hackers
jakevdp
799
220k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.6k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
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͔ͩΒͦ͜ʯͷՁΛఏڙ͠·͢