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
15k
14
Share
React.js, Draft.jsで作る リッチテキストエディタ開発入門
Meguro.es #4 @wantedlyでのトーク内容です
mottox2
June 21, 2016
More Decks by mottox2
See All by mottox2
つくり方を変えていく | change-how-we-build
mottox2
2
1.1k
もう一歩進めたい OG画像の動的生成
mottox2
7
2.5k
なぜコピペで使うコンポーネント集を利用するのか?
mottox2
8
7.5k
UIコンポーネントライブラリをうまく使うためにできること / components-with-designer
mottox2
7
4k
Figma Plugin公開までの壁を乗り越える
mottox2
3
4k
Puppeteerでつくる画像と動画 / images and videos made with puppeteer
mottox2
0
770
手触りのよいウェブを考える / better-mobile-web
mottox2
3
2k
組織と権限とSlack App / slack-app-with-roles
mottox2
1
710
SSRを避けるためにやっていること / ssr-alternative
mottox2
9
3.3k
Other Decks in Programming
See All in Programming
Claude Code × Gemini × Ebitengine ゲーム制作素人WebエンジニアがGoでゲームを作った話
webzawa
0
130
How Swift's Type System Guides AI Agents
koher
0
250
今こそ押さえておきたい アマゾンウェブサービス(AWS)の データベースの基礎 おもクラ #6版
satoshi256kbyte
1
240
ふりがな Deep Dive try! Swift Tokyo 2026
watura
0
210
CursorとClaudeCodeとCodexとOpenCodeを実際に比較してみた
terisuke
1
450
レガシーPHP転生 〜父がドメインエキスパートだったのでDDD+Claude Codeでチート開発します〜
panda_program
0
900
LM Linkで(非力な!)ノートPCでローカルLLM
seosoft
0
490
Don't Prompt Harder, Structure Better
kitasuke
0
750
Lightning-Fast Method Calls with Ruby 4.1 ZJIT / RubyKaigi 2026
k0kubun
1
200
Kingdom of the Machine
yui_knk
1
110
実用!Hono RPC2026
yodaka
2
190
2026_04_15_量子計算をパズルとして解く
hideakitakechi
0
100
Featured
See All Featured
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
1.9k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
0
200
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
260
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
790
The Cost Of JavaScript in 2023
addyosmani
55
9.8k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
200
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Building Adaptive Systems
keathley
44
3k
Building Applications with DynamoDB
mza
96
7k
Are puppies a ranking factor?
jonoalderson
1
3.3k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.9k
A Modern Web Designer's Workflow
chriscoyier
698
190k
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͔ͩΒͦ͜ʯͷՁΛఏڙ͠·͢