Building Apps with React.js & Flux
第56回 HTML5とか勉強会(JavaScriptフレームワーク最前線 - AngularJS、React.js - )
https://html5j.doorkeeper.jp/events/23074
#VJMEJOH"QQTXJUI3FBDUKT'MVYୈճ)5.-ͱ͔ษڧձ LPCB
View Slide
LPCBw 8FC"QQMJDBUJPO&OHJOFFSw IUUQLPCBDPNw Ұਓ3FBDUKT"EWFOU$BMFOEBSw IUUQRJJUBDPNBEWFOUDBMFOEBSSFBDUKT
3FBDUKTw GBDFCPPL͕࡞ͬͨ+BWB4DSJQUͷ6*ϥΠϒϥϦw +6455)&6*w 7*356"-%0.w %"5"'-08
6TJOH3FBDUKTw 'BDFCPPLw GBDFCPPLDPN NFTTFOHFSDPN JOTUBHSBNDPNw /FUqJYw ##$w "UMBTTJBOw ʜ
8IZ3FBDUKTw ෳࡶͳϢʔβʔΠϯλʔϑΣΠεΛߏங͢ΔͨΊʹw ։ൃޮͷ্Ͱͳͯ͘3FMJBCJMJUZ ৴པੑΛ֬อ͢Δ͜ͱ͕త
)FMMPimport React from ‘react’;class Hello extends React.Component {render() {return Hello {this.props.name};}}React.render(,document.getElementById(‘app’));// Hello World
)FMMPimport React from ‘react’;class Hello extends React.Component {render() {return Hello {this.props.name};}}React.render(,document.getElementById(‘app’));// Hello Worldʂʂʂʁʁʁ
+49w 4ZOUBYTVHBSPG3FBDUDSFBUF&MFNFOUw +BWB4DSJQUͷදݱੑͱ5FNQMBUFͷΘ͔Γ͢͞w 4FQBSBUJPOPGDPODFSOTHello!React.createElement(“div”, {className: “foo”}, “Hello”)W͔Β࠷దԽ
$PNQPOFOUw දࣔཁૉΛͦΕͧΕ$PNQPOFOUͱׂͯͦ͠͠ͷதʹৼΔ͍ͱݟͨΛ·ͱΊΔw $PIFTJPO ڽूˢɺ$PVQMJOH ݁߹ˣ
4UBUFMFTTw ֤$PNQPOFOUʹঢ়ଶΛ࣋ͨͤΔͷͰͳ͘ɺ$PNQPOFOU͕ঢ়ଶΛཧ͢Δw .VUBCMF มߋՄೳͳෳࡶੑΛੜΉ
-JLF4FSWFSTJEFSFOEFSJOHw ঢ়ଶ͕มΘͬͨΒͷ$PNQPOFOU͔Βશͯͷ$PNQPOFOUΛ࠶࡞Γ͢w 1FSGPSNBODF ˠ͚ࠩͩΛ%0.ʹө
ᶃUSJHHFSᶄFWFOUᶅVQEBUFᶆEBUBᶇVQEBUF%0.
%PPNFOHJOFIUUQXXXTMJEFTIBSFOFUqPZEPQIPOFSFBDUQSFTPW
1SPQ4UBUF
1SPQ4UBUFw 1SPQw $PNQPOFOUͷ*OUFSGBDFw *NNVUBCMFw 4UBUFw $PNQPOFOU͕0XOFSTIJQΛ࣋ͭw ࢠʹ1SPQͱͯ͢͠
1SPQ EBUB1SPQ EBUB1SPQ FWFOUᶃUSJHHFSᶅVQEBUF4UBUF1SPQ FWFOU
ਆࢿྉIUUQTTQFBLFSEFDLDPNIPLBDDIBJOUSPEVDUJPOUPSFBDU
-FU`TTUBSU
$PNQPOFOU
App InputFilterHNStoryHNStories
$PNQPOFOU*'
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);}}QBTTBTQSPQTIBTBTUBUFSFDFJWFQSPQT
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)}/>)}}MJTUFOUIFDIBOHFFWFOUEFMFHBUFUIFFWFOU
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
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}commentslink);}}
React.render(, document.getElementById('app'));
6QEBUF4UBUF
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)});})})});}}
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);}}
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)}/>)}}
HJUIVCDPNLPCBSFBDUIBDLFSOFXTTUPSJFT
$PNQMFY"QQMJDBUJPO
[email protected]
'MVYIUUQTHJUIVCDPNGBDFCPPLqVY
'MVYw "SDIJUFDUVSFw 6OJEJSFDUJPOBM%BUBqPXw $PNQPOFOU"DUJPO$SFBUPS%JTQBUDIFS4UPSF$PNQPOFOU
$PNQPOFOU"DUJPO$SFBUPST "DUJPO$SFBUPST%JTQBUDIFS4UPSF 4UPSF$PNQPOFOU $PNQPOFOU0UIFST)551ʜ.FUIPE"DUJPO"DUJPO&WFOUEBUB
-FU`THPUPBqVYUSJQ
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ΛݺͿ
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ΛݺͿ
"DUJPO$SFBUPST
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
%JTQBUDIFS
import {Dispatcher} from 'flux';const AppDispatcher = new Dispatcher();શͯͷ"DUJPOΛड͚ͯશͯͷ4UPSFXBJU'PSͰॱ൪ͷ੍ޚ
4UPSF
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Λड͚ͯඞཁͳͷ͚ͩॲཧ͢Δ
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
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Λड͚ͯߋ৽
"DUJPO$SFBUPST%JTQBUDIFS4UPSF$PNQPOFOU $PNQPOFOUFNJUFNJUMJTUFOMJTUFOEBUB EBUB
HJUIVCDPNLPCBSFBDUIBDLFSOFXTTUPSJFTUSFFqVY
'MVYDPNQBSJTPOw IUUQTHJUIVCDPNWPSPOJBOTLJqVYDPNQBSJTPOw 'BDFCPPL'MVYw 'MVYJCMFCZ:BIPPw 'MVNNPYw .BUFSJBM'MVYw 'MVYYPS
$PODMVTJPOw $PNQPOFOUʹΑΔΧϓηϧԽw ͔Βࢠͷ໌֬ͳσʔλͷྲྀΕw ෳࡶͳϢʔβʔΠϯλʔϑΣΠεͷ୯७Խ
5IBOLZPVTQFBLFSEFDLDPNLPCB