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
Dive into React.js
Search
koba04
February 15, 2015
Programming
10
3.4k
Dive into React.js
歌舞伎座.tech#6「VirtualDOMとReact」での発表資料です。
http://kbkz.connpass.com/event/11254/
koba04
February 15, 2015
Tweet
Share
More Decks by koba04
See All by koba04
フロントエンドの現在地とこれから
koba04
10
5.3k
Standing on the shoulders of giants
koba04
0
2.9k
React/Next によるアプリケーション開発のこれから
koba04
61
17k
フロントエンド刷新をプロジェクトとして進める際に気をつけていること
koba04
3
1.9k
How useEvent would change our applications
koba04
1
3.1k
kintoneフロントエンド刷新によるモノリスからの脱却とその先に目指す未来
koba04
3
15k
Make it Declarative with React
koba04
0
1.8k
Ready for React in 2019
koba04
2
1.7k
Algorithms in React
koba04
14
17k
Other Decks in Programming
See All in Programming
(Extension DC 2025) Actor境界を越える技術
teamhimeh
1
200
プロダクト開発をAI 1stに変革する〜SaaS is dead時代で生き残るために〜 / AI 1st Product Development
kobakei
0
440
Playwrightはどのようにクロスブラウザをサポートしているのか
yotahada3
7
2.2k
AIエージェント時代における TypeScriptスキーマ駆動開発の新たな役割
bicstone
4
1.4k
育てるアーキテクチャ:戦い抜くPythonマイクロサービスの設計と進化戦略
fujidomoe
1
150
株式会社 Sun terras カンパニーデック
sunterras
0
180
CSC305 Lecture 01
javiergs
PRO
1
380
Django Ninja による API 開発効率化とリプレースの実践
kashewnuts
0
870
エンジニアとして高みを目指す、 利益を生み出す設計の考え方 / design-for-profit
minodriven
23
11k
なぜGoのジェネリクスはこの形なのか? Featherweight Goが明かす設計の核心
ryotaros
7
1k
CSC509 Lecture 03
javiergs
PRO
0
320
LLMとPlaywright/reg-suitを活用した jQueryリファクタリングの実際
kinocoboy2
4
660
Featured
See All Featured
Balancing Empowerment & Direction
lara
4
670
Building a Modern Day E-commerce SEO Strategy
aleyda
43
7.7k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.1k
Making the Leap to Tech Lead
cromwellryan
135
9.5k
The Language of Interfaces
destraynor
162
25k
Learning to Love Humans: Emotional Interface Design
aarron
274
40k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
6.1k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
657
61k
The Cult of Friendly URLs
andyhume
79
6.6k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
285
14k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Gamification - CAS2011
davidbonilla
81
5.5k
Transcript
%JWFJOUP3FBDUKT Վب࠲UFDI !LPCB
!LPCB w 8FC"QQMJDBUJPO&OHJOFFS w IUUQLPCBDPN
None
3FBDUKT
http://facebook.github.io/react/
3FBDUKTFWFSZXIFSF
https://www.facebook.com/
http://instagram.com/
http://instagram.com/
https://speakerdeck.com/mridgway/isomorphic-flux
None
http://facebook.github.io/react/docs/videos.html
https://developer.atlassian.com/blog/2015/02/rebuilding-hipchat-with-react/
https://vivaldi.com/
http://techblog.netflix.com/2015/01/netflix-likes-react.html
3FBDUXPSLTXJUIPVUUIF%0. https://github.com/reactjs/react-art/blob/master/src/ReactART.js
http://engineering.flipboard.com/2015/02/mobile-web/
None
$PNQPOFOU
$PNQPOFOU w $PNQPOFOUΛ࡞ͬͯ͏ɻશͯ$PNQPOFOU୯ҐͰߟ͑Δɻ w EJW3FBDUKT͕ఆ͍ٛͯ͠Δ$PNQPOFOUɻ var Hello = React.createClass({ render()
{ return <div>Hello {this.props.name}</div>; } }); React.render(<Hello name=“kbkz” />, document.body); // <div>Hello kbkz</div>
&4$MBTT WCFUB class Hello extends React.Component { render() { return
<div>Hello {this.props.name}</div>; } } React.render(<Hello name=“kbkz” />, document.body); // <div>Hello kbkz</div>
$PNQPOFOU &MFNFOU ReactComponentClass = React.createClass({ render(){} }); ReactCompositeComponent = React.render(<ReactComponentClass
/>, document); ReactCompositeComponent.setState({}); ReactDOMComponent = React.render(<div />, document); ReactDOMComponent.setState({}); // Uncaught TypeError: undefined is not a function ReactElement = React.createElement(ReactComponentClass, null); // ReactElement has type, props(children), key, ref.
1SPQBOE4UBUF
1SPQJTJNNVUBCMF w 1SPQ$PNQPOFOU͕֎෦͔Βड͚͚ΔͰมߋෆՄɻ w 1SPQͷσʔλॴ༗͍ͯ͠ͳ͍σʔλɻ w )BOEMFSΛड͚͚ΔΑ͏ʹͯ͠ʹॲཧΛҕৡͨ͠Γɻ var Foods =
React.createClass({ render() { var foods = [‘sushi’, ‘ramen’, ‘pizza’].map( food => { return <Food name={food} /> }); return <div>{foods}</div>; } var Food = React.createClass({ render() { return <ul><li>{this.props.name}</li></ul> } }); ͜ͷσʔλ Foodsͷͷ
1SPQJTB4QFD w ֎෦ͱͷ*'༷ͱͳΔɻ w 1SPQ5ZQFTͰ໌֬ʹఆٛ͢Δɻ var Food = React.createClass({ propTypes:
{ name: React.PropTypes.string.isRequired }, render() { return <ul><li>{this.props.name}</li></ul> } }); จࣈྻͰඞਢ
1SPQ5ZQFT React.PropTypes.array // ྻ React.PropTypes.bool.isRequired // BooleanͰඞਢ React.PropTypes.func //
ؔ React.PropTypes.number // React.PropTypes.object // ΦϒδΣΫτ React.PropTypes.string // จࣈྻ React.PropTypes.node // RenderͰ͖Δͷ React.PropTypes.element.isRequired // React ElementͰඞਢ React.PropTypes.instanceOf(XXX) // XXXͷinstance͔Ͳ͏͔
1SPQ5ZQFT React.PropTypes.oneOf(['foo', 'bar']) // foo͔bar React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.array]) // จࣈྻ͔ྻ
React.PropTypes.arrayOf(React.PropTypes.string) // จࣈྻͷྻ͔Ͳ͏͔ React.PropTypes.objectOf(React.PropTypes.string) // จࣈྻͷΛ͍࣋ͬͯΔ͔ React.PropTypes.shape({ // ࢦఆ͞ΕͨܗࣜΛຬ͍ͨͯ͠Δ͔ color: React.PropTypes.string, fontSize: React.PropTypes.number }); React.PropTypes.any.isRequired // ͳΜͰ͍͍͚Ͳඞਢ // ΧελϜͷ੍ఆٛग़དྷΔ(μϝͳ߹Error͛Δ) customPropType: function(props, propName, componentName) { if (!/^[0-9]/.test(props[propName])) { return new Error('Validation failed!'); } }
4UBUFJTNVUBCMF w 4UBUFͦͷ$PNQPOFOU͕ঢ়ଶΛཧ͢Δɻ w TUBUFࣗମJNNVUBCMFͱͯ͠ѻ͍ඞͣTFU4UBUFͰߋ৽͢Δɻ var Counter = React.createClass({ getInitialState()
{ return { count: 0 }; }, onClick() { this.setState({ count: this.state.count + 1}); }, render() { return ( <div> <span>{this.state.count}</span> <button onClick={this.onClick}>click</button> </div> ); } });
1SFEJDUBCMF
0XOFSTIJQ w $PNQPOFOUؒͷࢠؔΛҙࣝ͢Δɻ Image Image Food Food Foods setState({ foods:
[ { name: “sushi”, img: “sushi.png” }, { name: “ramen”, img: “ramen.png”} ] }); <Food food={foods[1]} /> <Food food={foods[0]} /> <Image src=“ramen.png” /> <Image src=“sushi.png” /
3FVTFBCMF w $PNQPOFOUͷׂΛҙࣝ͢Δɻ Image Image Food Food Foods click! onClick()
{ this.props.onImageClick(); } onImageClick() { this.props.onFoodClick(this.props.food); }; onFoodClick(food) { : this.setState({ foods: newFoods }); };
3FSFOEFSUIFFOUJSFBQQ w 3FBDUKT͕%0.ͷࠩߋ৽Λͬͯ͘ΕΔͷͰɺΞϓϦͷσʔ λΛ·ͱΊ͓͍ͯ࣋ͬͯͯTFU4UBUFͰߋ৽͢ΔΑ͏ͳࡶͳΞʔ ΩςΫνϟՄೳʹͳΔɻ
7JSUVBM%0.JTJOGSBTUSVDUVSF w 7JSUVBM%0.ͷ࣮͍ͭͯ͏ଆؾʹ͢Δඞཁͳ͍ɻ w Ϧετʹର͢ΔLFZͷࢦఆ͘Β͍ɻ w ܭࢉྔΛݮΒͨ͢Ίʹ৭ʑͱͯͨ͠Γ͢Δɻ w ಉ֊ɺ$PNQPOFOUಉ࢜Ͱͷൺֱʜ w
IUUQDBMFOEBSQFSGQMBOFUDPNEJ⒎ render() { var li = this.state.items.map((item) => <li key={item.id}>{item.name}</li> ); return <ul>{li}</ul>; }
-JGFDZDMF
$PNQPOFOUMJGFDZDMF w .PVOUJOH w DPNQPOFOU8JMM.PVOU DPNQPOFOU%JE.PVOU w 6QEBUJOH w
DPNQPOFOU8JMM3FDFJWF1SPQT TIPVME$PNQPOFOU6QEBUF DPNQPOFOU8JMM6QEBUF DPNQPOFOU%JE6QEBUF w 6ONPVOUJOH w DPNQPOFOU8JMM6ONPVOU
+49
+49 w +VTUTZOUBYTVHBSPG3FBDUDSFBUF&MFNFOU w +49JTPQUJPOBM w 4FQBSBUJPOPGDPODFSOT 㱠UFDIOPMPHJFT w
4QSFBE"UUSJCVUFT <div className=“container”>{this.props.name}</div> ! React.createElement("div", {className: "container"}, this.props.name); <User {...this.props} type=“user”} />
SFBDUUPPMT w KTYίϚϯυมϥΠϒϥϦ͕ೖ͍ͬͯΔɻ w CSPXTFSJGZͷUSBOTGPSNͰ͋ΔSFBDUJGZɺXFCQBDLͷMPBEFS Ͱ͋ΔKTYMPBEFSͰΘΕ͍ͯΔɻ w IBSNPOZPQUJPOΛ༗ޮʹ͢Δ͜ͱͰҰ෦ͷ&4 GFBUVSFΛ ͏͜ͱ͕ग़དྷΔɻ
#BCFM UP w #BCFMࣗମ͕+49Λαϙʔτ͍ͯ͠ΔͷͰ#BCFM͚ͩͰSFBDU UPPMT͏ඞཁ͕ͳ͍ɻ w IUUQCMPHLPCBDPNQPTUB DPNCJOBUJPOPGSFBDUKTBOEUP w CSPXTFSJGZ
CBCFMJGZɺXFCQBDL CBCFMMPBEFS͚ͩͰ 0,ɻ w +49ͷQBSTFSʹ33FWFSTFSBDPSOKTYΛ͍ͬͯΔɻ
#SPXTFSFOWJSPONFOU
SFGHFU%0./PEF w SFG$PNQPOFOUʹର͢ΔࢀরΛऔಘ͢ΔͨΊͷ1SPQɻ w HFU%0./PEFͱΈ߹Θͤͯ͏ͷ͘Β͍ʹʜɻ w HFU%0./PEF%0.ͷࢀরΛऔಘ͢ΔͨΊͷ"1*ɻ w W͔Β3FBDUpOE%0./PEFʹɻ onComponentDidMount()
{ this.refs.input.getDOMNode().focus(); }, render() { return <input type=“text” ref=“input” />; }
EBOHFSPVTMZ4FU*OOFS)5.- w )5.-Λͦͷ··͍ͨ͠ͱ͖ʹ͏ɻ w ҙͯ͠͏͜ͱΛڧௐͨ͠*'ɻ w ໊લมΘΔ͔ɻ *TTVFͰٞத w
IUUQTHJUIVCDPNGBDFCPPLSFBDUJTTVFT createMarkup(data) { return { __html: someEncodeAPI(data) }; }, render() { return <div dangerouslySetInnerHTML={this.createMarkup(data)} /> }
4ZOUIFUJD&WFOU w ࣮ࡍʹ&WFOU-JTUFOFSΛొ͍ͯ͠ΔͷSPPUͷཁૉʹ͚ͩͰɺ σϦήʔτʹΑ֤ͬͯ$PNQPOFOUͷΠϕϯτͱͯ͠Ϛοϐϯά ͍ͯ͠Δɻ onChange(e) { this.setState({ text: e.target.value
}); } onClick(e) { e.preventDefault(); } render() { return ( <div> <input type=“text” onChange={this.onChange} />; <a onClick={this.onClick}>no longer</a> </div> ); } Autobinding Not support in Class syntax
)PXUPUFTU
)PXUPUFTU w 3FBDUBEEPOT5FTU6UJMTSFOEFS*OUP%PDVNFOUͰ%0.ʹՃ ͯ͠ॻ͍͍ͯ͘ײ͡ɻ describe("handleSubmit", () => { let inputArtist,
preventDefault; beforeEach(() => { inputArtist = React.addons.TestUtils.renderIntoDocument(<InputArtist />); preventDefault = jest.genMockFunction(); inputArtist.setState({ inputArtist: 'travis' }); React.addons.TestUtils.Simulate.submit(inputArtist.getDOMNode(), { preventDefault: preventDefault }); }); it ("calls fetchByArtist with state.inputArtist", () => { expect(AppTracksActionCreators.fetchByArtist).toBeCalled(); expect(AppTracksActionCreators.fetchByArtist).toBeCalledWith('travis'); }); it ("calls e.preventDefault", () => { expect(preventDefault).toBeCalled(); });
3PVUJOH
SBDLUSFBDUSPVUFS w $PNQPOFOUͰϧʔςΟϯάΛఆ͍ٛͯ͘͠ɻ w TFSWFSTJEFSFOEFSJOHωετͨ͠ϧʔςΟϯάͳͲʹର Ԡ͍ͯͯ͠ଟػೳɻ var routes = (
<Route name="top" handler={App} path="/"> <Route name="artist" handler={Artist} /> <Route name="country" handler={Country} /> <DefaultRoute handler={Top} /> </Route> ); Router.run(routes, Router.HistoryLocation, (Handler) => { React.render(<Handler />, document); });
4FSWFSTJEFSFOEFSJOH
4FSWFSTJEFSFOEFSJOH w 3FBDUKTͰɺ$PNQPOFOUͷঢ়ଶΛΦϒδΣΫτͱͯ࣋ͬ͠ ͍ͯͯڥʹΑΒͣ)5.-ͱͯ͠ు͖ग़͢͜ͱ͕ग़དྷΔɻ w 4&0ɺJOJUJBMMPBEͷͨΊʹɻ w TFSWFSଆͰͷ+49ͷύʔεOPEFKTYΛͬͨΓUP UP OPEFDPNNBOE
ΛͬͨΓɻ w 4UPSFͷσʔλΛ4FSWFSͱ#SPXTFSͰڞ༗͢Δɻ
)PXUPJNQMFNFOU w SFOEFS5P4USJOH w ϑϩϯτଆͰ3FBDUKTΛ͍͍ͨ߹ʹ͏ɻEBUB SFBDUJEͳͲ͕Ճ͞Εͨ)5.-͕ੜ͞ΕΔɻ w ϝΠϯͬͪ͜ɻ w SFOEFS5P4UBUJD.BSLVQ
w EBUBSFBDUJEͳͲ͕͍ͯͳ͍୯७ͳ)5.-Λฦ͢ͷͰ੩త ͳϖʔδΛੜ͍ͨ͠߹ʹ͏ɻ
3FBDUSFOEFS5P4USJOH server HTTP Request data-reactid, data-react-checksum&ॳظσʔλ ͖ͷHTML React.renderͰੜͨ͠HTMLͱserver͔Βͷdata-react-checksumΛൺֱ React.render Ұக͢ΕEventListenerΛઃఆ͢Δ͚ͩͰɺ͠ͳ͍߹React.renderͰ
ੜͨ͠HTMLΛinnerHTML্ͯ͠ॻ͖ͯ͠DOMΛ࠶ߏங͢Δ
'MVY
'MVYJTBBSDIJUFDUVSF https://github.com/facebook/flux
'MVYJTBBSDIJUFDUVSF w GBDFCPPLqVYʹEJTQBUDIFS͔͠ͳ͍ɻ w ͦͷଞ4UPSF͕&WFOU&NJUUFSܧঝͯ͠Δ͘Β͍ɻ w ΑΓগͳ͍ίʔυͰॻ͖ͨ͘͢͠ΓɺTFSWFSTJEFSFOEFSJOH ʹରԠ͢ΔͨΊʹɺ৭ʑͳ'MVY࣮͕ཚཱ͍ͯ͠Δɻ w IUUQTHJUIVCDPNWPSPOJBOTLJqVYDPNQBSJTPO
w TFOTJUJWFͳ෦3FBDUKT͕໘Λݟͯ͘ΕΔͷͰɺ'MVYͰΞ ϓϦέʔγϣϯͷॲཧͷྲྀΕΛ୯७ʹɻ
%JTQBUDIFS import {Dispatcher} = from ‘flux’; import assign = from
‘object-assign’; import AppConstants = from ‘../constants/AppConstants’; ; let PayloadSources = AppConstants.PayloadSources; export default assign(new Dispatcher(), { handleViewAction: function(action) { this.dispatch({ source: PayloadSources.VIEW_ACTION, action: action }); } });
4UPSF let tracks = []; let TrackStore = assign({}, EventEmitter.prototype,
{ emitChange: function() { this.emit(CHANGE_EVENT); }, addChangeListener: function(callback) { this.on(CHANGE_EVENT, callback); }, removeChangeListener: function(callback) { this.removeListener(CHANGE_EVENT, callback); }, getAll: function() { return tracks; }, }); TrackStore.dispatchToken = AppDispatcher.register(function(payload) { let action = payload.action; switch (action.type) { case ActionTypes.RECEIVE_TRACKS_BY_ARTIST: tracks = action.tracks; TrackStore.emitChange(); break;
"DUJPO$SFBUPST export default { fetchByArtist: function(artist) { request.get( `url${encodeURIComponent(artist)}`, (res)
=> { AppDispatcher.handleViewAction({ type: ActionTypes.RECEIVE_TRACKS_BY_ARTIST, tracks: res.body.toptracks.track }); } ); }, }
7JFX export default React.createClass({ getInitialState() { return { tracks: TrackStore.getAll(),
}; }, componentDidMount: function() { TrackStore.addChangeListener(this._onChange); }, componentWillUnmount: function() { TrackStore.removeChangeListener(this._onChange); }, _onChange: function() { this.setState({ tracks: TrackStore.getAll() }); }, handleSubmit(e) { e.preventDefault(); let artist = this.state.inputArtist; if (artist) AppTracksActionCreators.fetchByArtist(artist); }, });
$PODMVTJPO
3FBDUKTJTGBTU
"EWBOUBHFPG7JSUVBM%0. w ͍ʁ w K2VFSZͳͲͰ%0.৮ͬͯॻ͍ͨํ͕͍ɻ w %0.ΛҙࣝͤͣʹࡶʹൣғͰ$PNQPOFOUΛߋ৽ͯͦ͠ ͍ͦ͜͜ɻ w TIPVME$PNQPOFOU6QEBUFʹΑΔ࠷దԽɻ
3FBDUKTJTFBTZ
.BLFEFWFMPQNFOUFBTJFS w %0.ͷөΛ3FBDUKTʹͤΔ͜ͱͰΞϓϦέʔγϣϯͷ ίʔυΛॻ͘͜ͱʹྗग़དྷΔɻ w +BWB4DSJQUͷࣝΛͦͷ··׆͔ͨ͠։ൃɻ
$PODMVTJPO w 3FBDUKT։ൃΛߴΊΔ͜ͱ͕తͰͳͯ͘ɺΞϓϦ έʔγϣϯͷ৴པੑΛߴΊΔ͜ͱ͕తɻ w 7JFXͷߋ৽Λ3FBDUKTʹ͓ͤͯ͘͜ͱͰΞϓϦέʔγϣϯͷ ίʔυΛγϯϓϧʹॻ͘͜ͱ͕ग़དྷΔɻ w $PNQPOFOUΛ࡞Δ͚ͩͳͷͰϥΠϒϥϦͷαΠζ͋Δͷ ͷಋೖ͍͢͠ɻ
w 3FBDUKTࠓΘΕ͍ͯΔٕज़ɻ
5IBOLZPV