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

Building with React.js & Flux

45daf58c77e9dbbab5a1c8a5afc7ac5c?s=47 koba04
April 28, 2015

Building with React.js & Flux

Building Apps with React.js & Flux

第56回 HTML5とか勉強会(JavaScriptフレームワーク最前線 - AngularJS、React.js - )

https://html5j.doorkeeper.jp/events/23074

45daf58c77e9dbbab5a1c8a5afc7ac5c?s=128

koba04

April 28, 2015
Tweet

Transcript

  1. #VJMEJOH"QQTXJUI3FBDUKT'MVY ୈճ)5.-ͱ͔ษڧձ  LPCB

  2. LPCB w 8FC"QQMJDBUJPO&OHJOFFS w IUUQLPCBDPN w Ұਓ3FBDUKT"EWFOU$BMFOEBS w IUUQRJJUBDPNBEWFOUDBMFOEBSSFBDUKT

  3. None
  4. 3FBDUKT w GBDFCPPL͕࡞ͬͨ+BWB4DSJQUͷ6*ϥΠϒϥϦ w +6455)&6* w 7*356"-%0. w %"5"'-08

  5. 6TJOH3FBDUKT w 'BDFCPPL w GBDFCPPLDPN NFTTFOHFSDPN JOTUBHSBNDPN w /FUqJY w

    ##$ w "UMBTTJBO w ʜ
  6. 8IZ3FBDUKT w ෳࡶͳϢʔβʔΠϯλʔϑΣΠεΛߏங͢ΔͨΊʹ w ։ൃޮ཰ͷ޲্Ͱ͸ͳͯ͘3FMJBCJMJUZ ৴པੑ Λ֬อ ͢Δ͜ͱ͕໨త

  7. )FMMP import React from ‘react’; class Hello extends React.Component {

    render() { return <div>Hello {this.props.name}</div>; } } React.render( <Hello name=“World” />, document.getElementById(‘app’) ); // <div>Hello World</div>
  8. )FMMP import React from ‘react’; class Hello extends React.Component {

    render() { return <div>Hello {this.props.name}</div>; } } React.render( <Hello name=“World” />, document.getElementById(‘app’) ); // <div>Hello World</div> ʂʂʂʁʁʁ
  9. +49 w 4ZOUBYTVHBSPG3FBDUDSFBUF&MFNFOU w +BWB4DSJQUͷදݱੑͱ5FNQMBUFͷΘ͔Γ΍͢͞ w 4FQBSBUJPOPGDPODFSOT <div className=“foo”>Hello</div> !

    React.createElement(“div”, {className: “foo”}, “Hello”) W͔Β ࠷దԽ΋
  10. $PNQPOFOU w දࣔཁૉΛͦΕͧΕ$PNQPOFOUͱͯ͠෼ׂͦ͠ͷத ʹৼΔ෣͍ͱݟͨ໨Λ·ͱΊΔ w $PIFTJPO ڽू౓ ˢɺ$PVQMJOH ݁߹౓ ˣ

  11. 4UBUFMFTT w ֤$PNQPOFOUʹঢ়ଶΛ࣋ͨͤΔͷͰ͸ͳ͘ɺ਌ $PNQPOFOU͕ঢ়ଶΛ؅ཧ͢Δ w .VUBCMF มߋՄೳ ͳ஋͸ෳࡶੑΛੜΉ

  12. -JLF4FSWFSTJEFSFOEFSJOH w ঢ়ଶ͕มΘͬͨΒ਌ͷ$PNQPOFOU͔Βશͯͷ $PNQPOFOUΛ࠶౓࡞Γ௚͢ w 1FSGPSNBODF ˠࠩ෼͚ͩΛ%0.ʹ൓ө

  13. ᶃUSJHHFS ᶄFWFOU ᶅVQEBUF ᶆEBUB ᶇVQEBUF%0.

  14. %PPNFOHJOF IUUQXXXTMJEFTIBSFOFUqPZEPQIPOFSFBDUQSFTPW

  15. 1SPQ4UBUF

  16. 1SPQ4UBUF w 1SPQ w $PNQPOFOUͷ*OUFSGBDF w *NNVUBCMF w 4UBUF w

    $PNQPOFOU͕0XOFSTIJQΛ࣋ͭ஋ w ࢠʹ1SPQͱͯ͠౉͢
  17. 1SPQ EBUB 1SPQ EBUB 1SPQ FWFOU ᶃUSJHHFS ᶅVQEBUF4UBUF 1SPQ FWFOU

  18. ਆࢿྉ IUUQTTQFBLFSEFDLDPNIPLBDDIBJOUSPEVDUJPOUPSFBDU

  19. -FU`TTUBSU

  20. None
  21. $PNQPOFOU

  22. App InputFilter HNStory HNStories

  23. $PNQPOFOU*'

  24. class App extends React.Component { constructor(props) { super(props); this.state =

    { stories: [], filterText: ‘’}; } static get propTypes() { return { count: React.PropTypes.number } } static getDefaultProps() { return { count: 50 } } handleFilter(input) {} render() { return ( <div> <h1>HackerNews Stories</h1> <InputFilter handleFilter={this.handleFilter.bind(this)} /> <HNStories stories={this.state.stories} /> </div> ); } } QBTTBTQSPQT IBTBTUBUF SFDFJWFQSPQT
  25. class InputFilter extends React.Component { static get propTypes() { return

    { handleFilter: React.PropTypes.func.isRequired } } onInput(e) { this.props.handleFilter(e.target.value); } render() { return ( <div> <input type="text" placeholder="filter" onChange={this.onInput.bind(this)} /> </div> ) } } MJTUFOUIFDIBOHFFWFOU EFMFHBUFUIFFWFOU
  26. class HNStories extends React.Component { static get propTypes() { return

    { stories: React.PropTypes.arrayOf(React.PropTypes.object).isRequired } } render() { return ( <div> {this.props.stories.map( story => <HNStory key={story.id} story={story} /> )} </div> ); } } QBTTBTQSPQT
  27. class HNStory extends React.Component { static get propTypes() { return

    { story: React.PropTypes.object.isRequired } } render() { const {rank, url, title, by, kids} = this.props.story; const commentCount = kids ? kids.length : 0; return ( <div> <div>{rank}</div> <div> {title} </div> <div> <span>by {by}</span> <span>{commentCount}comments</span> <span><a href={url} target="_blank">link</a></span> </div> </div> ); } }
  28. React.render(<App count={100} />, document.getElementById('app'));

  29. 6QEBUF4UBUF

  30. class App extends React.Component { : componentDidMount() { fetch(HN_TOP_STORY_URL) .then(res

    => res.json()) .then(ids => { ids.slice(0, this.props.count).forEach((id, index) => { fetch(`${HN_STORY_URL}/${id}.json`) .then(res => res.json()) .then(story => { story.rank = index + 1; this.setState({ stories: this.state.stories.concat([story]).sort((a,b) => a.rank - b.rank) }); }) }) }); } }
  31. class App extends React.Component { : handleFilter(input) { this.setState({filterText: input});

    } filterStories() { const filterText = this.state.filterText.toLowerCase(); return this.state.stories.filter(story => { return !filterText || story.title.toLowerCase().indexOf(filterText) !== -1 || story.by.toLowerCase().indexOf(filterText) !== -1 ; }); } render() { return ( <div style={style.root}> <h1>HackerNews Stories</h1> <InputFilter handleFilter={this.handleFilter.bind(this)} /> <HNStories stories={this.filterStories()} /> </div> ); } }
  32. class InputFilter extends React.Component { static get propTypes() { return

    { handleFilter: React.PropTypes.func.isRequired } } onInput(e) { this.props.handleFilter(e.target.value); } render() { return ( <div> <input type="text" placeholder="filter" onChange={this.onInput.bind(this)} /> </div> ) } }
  33. HJUIVCDPNLPCBSFBDUIBDLFSOFXTTUPSJFT

  34. $PNQMFY"QQMJDBUJPO

  35. None
  36. IUUQTUXJUUFSDPNU@XBEBTUBUVT

  37. 'MVY IUUQTHJUIVCDPNGBDFCPPLqVY

  38. 'MVY w "SDIJUFDUVSF w 6OJEJSFDUJPOBM%BUBqPX w $PNQPOFOU"DUJPO$SFBUPS%JTQBUDIFS 4UPSF$PNQPOFOU

  39. $PNQPOFOU "DUJPO$SFBUPST "DUJPO$SFBUPST %JTQBUDIFS 4UPSF 4UPSF $PNQPOFOU $PNQPOFOU 0UIFST )551ʜ

    .FUIPE "DUJPO "DUJPO &WFOU EBUB
  40. App InputFilter HNStory HNStories

  41. -FU`THPUPBqVYUSJQ

  42. $PNQPOFOU

  43. class App extends React.Component { constructor(props) { super(props); this.state =

    {stories: HNStoriesStore.filteredStrories()}; this._onChange = this._onChange.bind(this); } static get propTypes() { return { count: React.PropTypes.number}} static getDefaultProps() { return {count: 50} } componentDidMount() { HNStoriesStore.addChangeListener(this._onChange); AppHNStoriesActionCreators.fetch(this.props.count); } componentWillUnmount() { HNStoriesStore.removeChangeListener(this._onChange); } _onChange() { this.setState({ stories: HNStoriesStore.filteredStrories()}); } render() { return ( <div> <h1>HackerNews Stories</h1> <InputFilter /> <HNStories stories={this.state.stories} /> </div> ) } } "DUJPO$SFBUPSΛݺͿ
  44. class InputFilter extends React.Component { onInput(e) { AppHNStoriesActionCreators.filter(e.target.value); } render()

    { return ( <div> <input type="text" placeholder="title" onChange={this.onInput.bind(this)} /> </div> ) } } "DUJPO$SFBUPSΛݺͿ
  45. "DUJPO$SFBUPST

  46. const Action = { fetch(count) { fetch(HN_TOP_STORY_URL) .then(res => res.json())

    .then(ids => { ids.slice(0, count).forEach((id, index) => { fetch(`${HN_STORY_URL}/${id}.json`) .then(res => res.json()) .then(story => { story.rank = index + 1; AppDispatcher.dispatch({ type: ActionTypes.RECEIVE_STORY, story: story }); }) }) }); }, filter(text) { AppDispatcher.dispatch({ type: ActionTypes.RECEIVE_FILTER_TEXT, text: text }); } }; "DUJPOΛൃߦͯ͠ %JTQBUDIFS΁
  47. %JTQBUDIFS

  48. import {Dispatcher} from 'flux'; const AppDispatcher = new Dispatcher(); શͯͷ"DUJPOΛड͚ͯ

    શͯͷ4UPSF΁ XBJU'PSͰॱ൪ͷ੍ޚ΋
  49. 4UPSF

  50. const HNStories = assign({}, EventEmitter.prototype, { : }); HNStories.dispatchToken =

    AppDispatcher.register(action => { switch (action.type) { case ActionTypes.RECEIVE_STORY: stories = stories.concat([action.story]); HNStories.emitChange(); break; case ActionTypes.RECEIVE_FILTER_TEXT: filterText = action.text.toLowerCase(); HNStories.emitChange(); break; } }); શͯͷ"DUJPOΛड͚ͯ ඞཁͳ΋ͷ͚ͩॲཧ͢Δ
  51. let stories = []; let filterText = ''; const HNStories

    = assign({}, EventEmitter.prototype, { emitChange() { this.emit(CHANGE_EVENT); }, addChangeListener(callback) { this.on(CHANGE_EVENT, callback); }, removeChangeListener(callback) { this.removeListener(CHANGE_EVENT, callback); }, filteredStrories() { return stories.filter(story => { return !filterText || story.title.toLowerCase().indexOf(filterText) !== -1 || story.by.toLowerCase().indexOf(filterText) !== -1 ; }); } }); : : $PNQPOFOUʹର͢Δ &WFOUͱHFUUFS
  52. $PNQPOFOU

  53. class App extends React.Component { constructor(props) { super(props); this.state =

    {stories: HNStoriesStore.filteredStrories()}; this._onChange = this._onChange.bind(this); } static get propTypes() { return { count: React.PropTypes.number}} static getDefaultProps() { return {count: 50} } componentDidMount() { HNStoriesStore.addChangeListener(this._onChange); AppHNStoriesActionCreators.fetch(this.props.count); } componentWillUnmount() { HNStoriesStore.removeChangeListener(this._onChange); } _onChange() { this.setState({ stories: HNStoriesStore.filteredStrories()}); } render() { return ( <div> <h1>HackerNews Stories</h1> <InputFilter /> <HNStories stories={this.state.stories} /> </div> ) } } 4UPSF͔ΒσʔλΛड͚औΓ &WFOUΛड͚ͯߋ৽
  54. "DUJPO$SFBUPST %JTQBUDIFS 4UPSF $PNQPOFOU $PNQPOFOU FNJU FNJU MJTUFO MJTUFO EBUB

    EBUB
  55. HJUIVCDPNLPCBSFBDUIBDLFSOFXTTUPSJFTUSFFqVY

  56. 'MVYDPNQBSJTPO w IUUQTHJUIVCDPNWPSPOJBOTLJqVYDPNQBSJTPO w 'BDFCPPL'MVY w 'MVYJCMFCZ:BIPP w 'MVNNPY w

    .BUFSJBM'MVY w 'MVYYPS
  57. $PODMVTJPO w $PNQPOFOUʹΑΔΧϓηϧԽ w ਌͔Βࢠ΁ͷ໌֬ͳσʔλͷྲྀΕ w ෳࡶͳϢʔβʔΠϯλʔϑΣΠεͷ୯७Խ

  58. 5IBOLZPV TQFBLFSEFDLDPNLPCB