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

Building with React.js & Flux

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

koba04

April 28, 2015
Tweet

More Decks by koba04

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

  3. View Slide

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

    View Slide

  5. 6TJOH3FBDUKT
    w 'BDFCPPL
    w GBDFCPPLDPN NFTTFOHFSDPN JOTUBHSBNDPN
    w /FUqJY
    w ##$
    w "UMBTTJBO
    w ʜ

    View Slide

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

    View Slide

  7. )FMMP
    import React from ‘react’;
    class Hello extends React.Component {
    render() {
    return Hello {this.props.name};
    }
    }
    React.render(
    ,
    document.getElementById(‘app’)
    );
    // Hello World

    View Slide

  8. )FMMP
    import React from ‘react’;
    class Hello extends React.Component {
    render() {
    return Hello {this.props.name};
    }
    }
    React.render(
    ,
    document.getElementById(‘app’)
    );
    // Hello World
    ʂʂʂʁʁʁ

    View Slide

  9. +49
    w 4ZOUBYTVHBSPG3FBDUDSFBUF&MFNFOU
    w +BWB4DSJQUͷදݱੑͱ5FNQMBUFͷΘ͔Γ΍͢͞
    w 4FQBSBUJPOPGDPODFSOT
    Hello
    !
    React.createElement(“div”, {className: “foo”}, “Hello”)
    W͔Β
    ࠷దԽ΋

    View Slide

  10. $PNQPOFOU
    w දࣔཁૉΛͦΕͧΕ$PNQPOFOUͱͯ͠෼ׂͦ͠ͷத
    ʹৼΔ෣͍ͱݟͨ໨Λ·ͱΊΔ
    w $PIFTJPO ڽू౓
    ˢɺ$PVQMJOH ݁߹౓
    ˣ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  14. %PPNFOHJOF
    IUUQXXXTMJEFTIBSFOFUqPZEPQIPOFSFBDUQSFTPW

    View Slide

  15. 1SPQ4UBUF

    View Slide

  16. 1SPQ4UBUF
    w 1SPQ
    w $PNQPOFOUͷ*OUFSGBDF
    w *NNVUBCMF
    w 4UBUF
    w $PNQPOFOU͕0XOFSTIJQΛ࣋ͭ஋
    w ࢠʹ1SPQͱͯ͠౉͢

    View Slide

  17. 1SPQ EBUB

    1SPQ EBUB

    1SPQ FWFOU

    ᶃUSJHHFS
    ᶅVQEBUF4UBUF
    1SPQ FWFOU

    View Slide

  18. ਆࢿྉ
    IUUQTTQFBLFSEFDLDPNIPLBDDIBJOUSPEVDUJPOUPSFBDU

    View Slide

  19. -FU`TTUBSU

    View Slide

  20. View Slide

  21. $PNQPOFOU

    View Slide

  22. App InputFilter
    HNStory
    HNStories

    View Slide

  23. $PNQPOFOU*'

    View Slide

  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 (

    HackerNews Stories



    );
    }
    }
    QBTTBTQSPQT
    IBTBTUBUF
    SFDFJWFQSPQT

    View Slide

  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 (

    type="text"
    placeholder="filter"
    onChange={this.onInput.bind(this)}
    />

    )
    }
    }
    MJTUFOUIFDIBOHFFWFOU
    EFMFHBUFUIFFWFOU

    View Slide

  26. class HNStories extends React.Component {
    static get propTypes() {
    return {
    stories: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
    }
    }
    render() {
    return (

    {this.props.stories.map(
    story =>
    )}

    );
    }
    }
    QBTTBTQSPQT

    View Slide

  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 (

    {rank}

    {title}


    by {by}
    {commentCount}comments
    link


    );
    }
    }

    View Slide

  28. React.render(, document.getElementById('app'));

    View Slide

  29. 6QEBUF4UBUF

    View Slide

  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)
    });
    })
    })
    });
    }
    }

    View Slide

  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 (

    HackerNews Stories



    );
    }
    }

    View Slide

  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 (

    type="text"
    placeholder="filter"
    onChange={this.onInput.bind(this)}
    />

    )
    }
    }

    View Slide

  33. HJUIVCDPNLPCBSFBDUIBDLFSOFXTTUPSJFT

    View Slide

  34. $PNQMFY"QQMJDBUJPO

    View Slide

  35. View Slide

  36. View Slide

  37. 'MVY
    IUUQTHJUIVCDPNGBDFCPPLqVY

    View Slide

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

    View Slide

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

    .FUIPE
    "DUJPO
    "DUJPO
    &WFOU
    EBUB

    View Slide

  40. App InputFilter
    HNStory
    HNStories

    View Slide

  41. -FU`THPUPBqVYUSJQ

    View Slide

  42. $PNQPOFOU

    View Slide

  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 (

    HackerNews Stories



    )
    }
    }
    "DUJPO$SFBUPSΛݺͿ

    View Slide

  44. class InputFilter extends React.Component {
    onInput(e) {
    AppHNStoriesActionCreators.filter(e.target.value);
    }
    render() {
    return (

    type="text"
    placeholder="title"
    onChange={this.onInput.bind(this)}
    />

    )
    }
    }
    "DUJPO$SFBUPSΛݺͿ

    View Slide

  45. "DUJPO$SFBUPST

    View Slide

  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΁

    View Slide

  47. %JTQBUDIFS

    View Slide

  48. import {Dispatcher} from 'flux';
    const AppDispatcher = new Dispatcher();
    શͯͷ"DUJPOΛड͚ͯ
    શͯͷ4UPSF΁
    XBJU'PSͰॱ൪ͷ੍ޚ΋

    View Slide

  49. 4UPSF

    View Slide

  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Λड͚ͯ
    ඞཁͳ΋ͷ͚ͩॲཧ͢Δ

    View Slide

  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

    View Slide

  52. $PNQPOFOU

    View Slide

  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 (

    HackerNews Stories



    )
    }
    }
    4UPSF͔ΒσʔλΛड͚औΓ
    &WFOUΛड͚ͯߋ৽

    View Slide

  54. "DUJPO$SFBUPST
    %JTQBUDIFS
    4UPSF
    $PNQPOFOU $PNQPOFOU
    FNJU
    FNJU
    MJTUFO
    MJTUFO
    EBUB EBUB

    View Slide

  55. HJUIVCDPNLPCBSFBDUIBDLFSOFXTTUPSJFTUSFFqVY

    View Slide

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

    View Slide

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

    View Slide

  58. 5IBOLZPV
    TQFBLFSEFDLDPNLPCB

    View Slide