Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React.js, Draft.jsで作る リッチテキストエディタ開発入門

React.js, Draft.jsで作る リッチテキストエディタ開発入門

Meguro.es #4 @wantedlyでのトーク内容です

297a42b94a1bda236982ec1cd81089b6?s=128

mottox2

June 21, 2016
Tweet

More Decks by mottox2

Other Decks in Programming

Transcript

  1. React.js, Draft.jsͰ࡞Δ ϦονςΩετΤσΟλ։ൃೖ໳ Meguro.es #4 @Wantedly ஛ຊ ༤و (@mottox2)

  2. γΰτͰίίϩΦυϧ ࣗݾ঺հ ஛ຊ ༤و @mottox2 • Wantedly৽ଔҰ೥໨ͷΤϯδχΞ • ϑϩϯτΤϯυ͕޷͖ •

    ʮϑΟʔυʯΛ࡞͍ͬͯ·͢
  3. දݱͷ෯Λ޿͛ΔͨΊʹ ϒϩάΤσΟλͷϦχϡʔΞϧΛߦͬͨ

  4. γΰτͰίίϩΦυϧ ϦονςΩετΤσΟλ։ൃ ϦονςΩετΤσΟλͷඞཁੑ ΤϯδχΞք۾ͰϚʔΫμ΢ϯʴϓϨϏϡʔ͕ྲྀߦ͍ͬͯΔɻ ී௨ͷਓ͔Βͨ͠ΒϦονςΩετΤσΟλ͕౰ͨΓલɻ ʮฤू͍ͯ͠Δݟͨ໨ʹެ։͞ΕΔ΋ͷʯͰ͋ͬͯཉ͍͠͸ͣɻ ϦονςΩετΤσΟλ։ൃͷਏ͞ (=contenteditableͱͷઓ͍) ᶃ ϒϥ΢βʹΑͬͯڍಈ͕ҧ͏

    ᶄ ཤྺ؅ཧ͕େม ᶅ DOMΛ৮Δඞཁ͕͋Δ
  5. γΰτͰίίϩΦυϧ ͜Ε·Ͱ ҎલͷΤσΟλ React+Reduxߏ੒ͷதͰɺੜ ͷDOMΛ͍͍ͬͯͨ͡ɻ ֦ுͣ͠Β͍ DOMΛ௚઀৮ͬͯ؅ཧ͍ͯ͠Δ෦෼

  6. γΰτͰίίϩΦυϧ Draft.jsͱ͸ʁ Facebookͷެ։͍ͯ͠ΔOSSɻ React.js্ͰϦονςΩετΤσΟλΛߏங͢ΔͨΊͷίϯϙʔωϯτΛఏڙ͠ ͯ͘ΕΔɻ https://github.com/facebook/draft-js Ըܙ ᶃ ϒϥ΢βؒͷڍಈͷࠩΛٵऩͯ͘͠ΕΔɻ ᶄ

    ΤσΟλશମΛ1ͭͷStateͱͯ͠؅ཧ͢Δ͔Βɺཤྺ؅ཧ΋؆୯ ᶅ DOM΋StateͰ͍࣋ͬͯΔͷͰɺJSΦϒδΣΫτͰ؅ཧͰ͖Δʢେ੾ʣ ߋʹɺReactίϯϙʔωϯτͰϦονͳදࣔ΋Ͱ͖Δɻ Draft.jsͷ࠾༻
  7. γΰτͰίίϩΦυϧ ී௨ͷ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} />; } }
  8. γΰτͰίίϩΦυϧ 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ͱಉ͡ײ֮Ͱ࢖͑Δ!͜ΕΛ֦ு͍ͯ͘͠
  9. γΰτͰίίϩΦυϧ 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)
  10. γΰτͰίίϩΦυϧ 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)
  11. γΰτͰίίϩΦυϧ 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Ͱ؅ཧΛߦ͏ɻ
  12. γΰτͰίίϩΦυϧ DOM௚઀͞ΘΓͨ͘ͳ͍໰୊ ղܾʂ ͍͍ײ͡ʹ৮Γ΍͍͢API͕ఏڙ͞Ε͍ͯΔͷͰ ޙ͸Block΍InlineStyle, EntityΛ͍͡Δ͚ͩͰྑ͍ɻ

  13. γΰτͰίίϩΦυϧ 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
  14. γΰτͰίίϩΦυϧ HTML΁ͷม׵ • GithubʹDraft.jsͷσʔλܗ͔ࣜΒHTML΍ASTʹม׵͢ ΔϥΠϒϥϦ͕ز͔ͭެ։͞Ε͍ͯΔɻ • WantedlyͰ͸RubyͰDraft.jsͷσʔλܗ͔ࣜΒHTMLʹ ม׵͢ΔίʔυΛॻ͍ͯରԠ͍ͯ͠Δɻ • iOS͕ωΠςΟϒΞϓϦͰهࣄΛදࣔ͢ΔํࣜΛ࠾༻ͯ͠

    ͓Γɺ΄΅Draft.jsͷσʔλܗࣜʹἧ͍͑ͯΔɻ
  15. γΰτͰίίϩΦυϧ Draft.jsͷਏ͍ͱ͜Ζ • ೔ຊʹ͋·Γ࢖͍ͬͯΔਓ͕͍ͳ͍ͷͰӳޠΛಡΉ͜ͱʹ ͳΔɻ • ࡉ͔͍ௐ੔Λ͢Δͱ͖ʹdraft.jsຊମͷίʔυಡΉඞཁ͕ ͋Δɻ • ϚϧνόΠτจࣈݻ༗ͷਏ͍ڍಈ͕͋Δɻ

    • ݁ہΩϟϨοτͷൣғ؅ཧͷਏ͞͸࢒Δɻ • σϑΥϧτͷվߦͷڍಈ͕޷ΈͰͳͯ݁͘ߏ͍ͬͨ͡ɻ
  16. γΰτͰίίϩΦυϧ ಋೖͯ͠Έͨײ૝ • Draft.jsΛಋೖ͢Δ͜ͱͰɺ։ൃָ͕ʹͳΔ ͭΒ͍ͱ͜Ζ͸͋Δ͚Ͳɺࠓ·ͰΑΓ͸͔ͳΓָɻ ։ൃָ͕ʹͳͬͨ෼ɺϢʔβʔ΋؆୯ʹهࣄΛ࡞ΕΔΑ͏ ʹ͍͖͍ͯͨ͠ɻ

  17. γΰτͰίίϩΦυϧ ࠷ޙʹ

  18. Wantedly FeedͰ ΤϯδχΞϒϩάΛॻ͖·͠ΐ͏ʂ ࠾༻ʹܨ͕ΔʮWantedly͔ͩΒͦ͜ʯͷՁ஋Λఏڙ͠·͢