Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
React速習会@Wantedly
Search
Kento Moriwaki
February 04, 2016
Technology
470
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
React速習会@Wantedly
社内でReactを浸透させるために行った速習会の資料
Kento Moriwaki
February 04, 2016
More Decks by Kento Moriwaki
See All by Kento Moriwaki
わかった気になれる CRDT を使った共同編集
kentomoriwaki
8
5.7k
デザインシステムを導入してUIに秩序を取り戻す - React (Native)編 #rejectron2018
kentomoriwaki
16
4k
ReactでWebとNativeの共通UIライブラリを作ろう
kentomoriwaki
0
1.3k
BFFを導入しなかった理由
kentomoriwaki
4
14k
TypeScript in Wantedly
kentomoriwaki
2
820
5分でわかる React "Suspense"
kentomoriwaki
3
1.5k
導入して1年経ったReact周辺の 技術スタックを反省します | React反省会@Wantedly
kentomoriwaki
10
8.8k
Immutable.jsとReact @Wantedly ~入門編~
kentomoriwaki
8
76k
Other Decks in Technology
See All in Technology
10年欲しかった音楽管理アプリを、AIと一緒に作りはじめた
judau
1
120
AIエージェントを安全で速い現場監督にする:Jev・Obsidian・メタハーネス
x5gtrn
PRO
0
120
地方移住と都心キャリアの両立は「金・時間・人」のリソースをフル活用すれば実現できる!〜Snowflake女子会 vol.8
snowwmn0824
0
120
【ゲームメーカーズスクランブル2026】『Shadowverse: Worlds Beyond』UIとアニメーションで実現する最高のユーザー体験を叶えるプロトタイピング
cygames
PRO
1
560
時うどん〜Socket.getifaddrsで学ぶネットワーク編 / Tokiudon: The Socket.getifaddrs Edition
coe401_
4
270
Mastering Agentic Development: Harness Engineering for Effective Coding Agents
konippi
3
580
Datadog で始める トークンセーフティなAI 導⼊と定着
vkbaba
1
100
Vibe Coding で作ったプロダクトをどう安全に動かすか / How to Safely Run Products Built with Vibe Coding
glidenote
0
700
Azure Serverless 2026:Production-ready な AI エージェント基盤 / Azure Serverless 2026: Production-Ready AI Agent Platform
miyake
2
610
Amazon Bedrock Agents ClassicからAmazon Bedrock AgentCoreへ移行した際、ガードレール設定が2箇所に割れた話
matsunobu
0
110
JSONataとAWS Step Functionsで目指すRuntimelessな世界
mu7889yoon
1
510
GitHub Agentic Workflows を触ってみる
htkym
2
660
Featured
See All Featured
Marketing to machines
jonoalderson
1
5.8k
Deep Space Network (abreviated)
tonyrice
0
320
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
New Earth Scene 8
popppiees
4
2.6k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
How Software Deployment tools have changed in the past 20 years
geshan
2
34k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
590
A better future with KSS
kneath
240
18k
Measuring & Analyzing Core Web Vitals
bluesmoon
9
1k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.1k
Transcript
Reactशձ@Wantedly 2016/02/04
୭ʁ • Kento Moriwaki • 2015य़ɺ৽ଔೖࣾ • ϑϩϯτΤϯυ͖ • Angular৮ͬͯͨ
ࠓΔ͜ͱ • React৮ͬͯΈΔ • Flux(Redux)ͯ͠ΈΔ • αʔόʔαΠυͰϨϯμϦϯάͯ͠ΈΔ • ଞͷใ
४උ git clone
[email protected]
:KentoMoriwaki/react_sokushu.git cd react_sokushu npm install npm install
-g webpack • Nodeݹ͗͢Δͱಈ͔ͳ͍͔ • v5.3.0Ͱ֬ೝ
४උ • Chrome dev toolͰReactͷσόοά͕ग़དྷΔ https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
Reactͱ(Α͘ฉ͘) • Facebook͕։ൃ • UIͷͨΊͷϥΠϒϥϦ • ϑϨʔϜϫʔΫͰͳ͍ • ԾDOM •
JSX
ͱΓ͋͑ͣಈ͔͢ git checkout component npm start webpack —watch • http://localhost:3000ʹΞΫηε
• ೖྗͰ͖ͳ͍ϑΥʔϜ͕දࣔ͞ΕΔ
components/App.js import React, { Component } from 'react' export default
class App extends Component { constructor(props) { super(props) this.state = { title: '', description: '' } } render() { return ( <form> <fieldset> <legend>Create Issue</legend> <input type="text" value={this.state.title} placeholder="Input title" /> <textarea value={this.state.description} placeholder="Input description" /> <button>Save</button> </fieldset> </form> ) } }
Component • Reactͷओ • View + Controller • ViewJSXͰɺrenderϝιουʹهड़͢Δ •
this.stateʹঢ়ଶΛอ࣋͢Δ
One-way data binding • ೖྗͯ͠σʔλมΘΒͳ͍ • ࣗͰ໌ࣔతʹมߋ͠ͳ͍ͱɺϏϡʔม ΘΒͳ͍ • σʔλ͕มΘΔͱϏϡʔ͕ࣗಈతʹมΘΔ
• this.setState(newState)Ͱมߋ
One-way data binding • onChange={} ʹϋϯυϥΛॻ͍ͯঢ়ଶΛมߋ͢Δ onChangeTitle(e) { this.setState({ title:
e.target.value }) } onChangeDescription(e) { this.setState({ description: e.target.value }) } render() { return ( <form> <fieldset> <legend>Create Issue</legend> <input type="text" value={this.state.title} onChange={this.onChangeTitle.bind(this)} /> <textarea value={this.state.description} onChange={this.onChangeDescription.bind(this)} /> <button>Save</button> </fieldset> </form> ) }
One-way data binding • σʔλ͕Ұํʹ͔͠ྲྀΕͳ͍ • ߟ͑Δ͜ͱ͕γϯϓϧʹͳΔ • σʔλ͔ΒͲ͏ͬͯϏϡʔΛ࡞Δ͔ •
σʔλΛͲ͏มߋ͢Δ͔
ෳίϯϙʔωϯτ • ೖྗͨ͠σʔλΛϦετͰද͍ࣔͨ͠ • git checkout list • ͖ͬ͞ͷAppΛIssueFormʹ •
৽͘͠IssueListίϯϙʔωϯτΛ࡞
ෳίϯϙʔωϯτ
components/App.js • IssueFormͰ࡞ΒΕͨΦϒδΣΫτΛIssueList ʹ͍ͨ͠ import React, { Component } from
'react' import IssueList from './IssueList' import IssueForm from './IssueForm' export default class App extends Component { render() { return ( <div> <IssueList /> <IssueForm /> </div> ) } }
Property • ίϯϙʔωϯτϓϩύςΟΛ࣋ͭ • JSXͷଐੑͱͯ͠ड͚औΕΔ • <IssueList issues={this.state.issues} /> •
ܕͷఆٛͳͲ͕ߦ͑Δ • PropTypesͱݺΕɺͲ͏͍͏ଐੑ໊ͰͲ͏͍͏ܕ ͷσʔλΛड͚औΔ͔ఆٛͰ͖Δ
components/App.js • IssueList͕issuesϓϩύςΟΛड͚औΔ export default class App extends Component {
constructor(props) { super(props) this.state = { issues: [ { id: 1, title: 'First Issue', description: 'foobarbaz' }, { id: 2, title: 'Incident', description: 'OMG' } ] } } render() { return ( <div> <IssueList issues={this.state.issues} /> <IssueForm /> </div> ) } }
components/App.js • IssueForm͕onSubmitϓϩύςΟΛड͚औΔ export default class App extends Component {
onSubmit(issue) { let issues = this.state.issues issue.id = issues.length + 1 this.setState({ issues: issues.concat(issue) }) } render() { return ( <div> <IssueList issues={this.state.issues} /> <IssueForm onSubmit={this.onSubmit.bind(this)} /> </div> ) } }
components/IssueList.js • this.props Ͱ͞ΕͨଐੑΛࢀরͰ͖Δ export default class IssueList extends Component
{ render() { return ( <table> { this.props.issues.map((issue) => { return ( <tr key={issue.id}> <td>{issue.id}</td> <td>{issue.title}</td> <td>{issue.description}</td> </tr> ) }) } </table> ) } }
components/IssueList.js • this.props Ͱ͞ΕͨଐੑΛࢀরͰ͖Δ export default class IssueList extends Component
{ constructor(props) { super(props) this.state = { title: '', description: '' } } onSubmit(e) { e.preventDefault() this.props.onSubmit(this.state) } render() { return ( <form onSubmit={this.onSubmit.bind(this)}> <fieldset> <legend>Create Issue</legend> <input type="text" value={this.state.title} …/> <textarea value={this.state.description} … /> <button>Save</button> </fieldset> </form> ) } }
StateͱProperty • StateMutable • PropertyImmutable • StateΛࢠComponentͷPropertyͱͯ͢͠ • ࢠComponent͔ΒState͕มߋ͞ΕΔ͜ͱͳ͍ •
มߋίʔϧόοΫͰ
Flux
Fluxͱ • Facebook͕ఏএͨ͠ΞʔΩςΫνϟ • ΞϓϦέʔγϣϯͱͯ͠σʔλϑϩʔҰํ ͚ͩʹྲྀΕΔ
MVC • Model͕ViewΛ࡞ΓɺView͕ModelΛมߋ͠ɺ͞Βʹ View͕มߋ͞ΕΔ
Flux • Action͕StoreΛมߋ͠ɺStore͕ViewΛͭ͘ΓɺView͕ ActionΛൃߦ͢Δ
ΣϒαΠτͱࣅͯΔ • యܕతͳΣϒαΠτ • αʔόʔ͕ϦΫΤετΛड͚औΓɺ DBͷσʔλΛߋ৽͢Δ • ৽͍͠σʔλΛݩʹϖʔδΛҰ͔ΒϨϯμϦϯά͢Δ • Flux
• Dispatcher͕ActionΛड͚औΓɺStoreΛߋ৽͢Δ • ৽͍͠StoreΛݩʹϖʔδΛҰ͔ΒϨϯμϦϯά͢Δ • ຊReact্͕ख͍͜ͱඞཁͳ෦͚ͩϨϯμϦϯάͯ͘͠ΕΔ
Flux࣮ • ΞʔΩςΫνϟͳͷͰ࣮͍Ζ͍Ζ • MVCϑϨʔϜϫʔΫ͕͍ͬͺ͍ଘࡏ͢ΔΈ ͍ͨʹ • ࠷ۙਓؾͷReduxΛ৮ͬͯΈΔ
Redux • Flux࣮ͷ̍ͭ • ಛ • ΞϓϦέʔγϣϯͷঢ়ଶΛ̍ͭͰѻ͏ • ঢ়ଶread-only •
มߋ७ਮͳؔͰॻ͚Δ • git checkout flux
Action • ΞΫγϣϯ໊ͱҾΛ·ͱΊͨΦϒδΣΫτ • ActionCreator • ActionΛฦؔ͢ export const ADD
= 'ADD_ISSUE' export const REFRESH = 'REFRESH_ISSUES' export function addIssue(issue) { return { type: ADD, issue: issue } } export function refreshIssues(issues) { return { type: REFRESH, issues: issues } }
Reducer • Actionͱݱࡏͷঢ়ଶΛड͚ͯɺ৽͍͠ঢ়ଶΛฦؔ͢ const initialState = [ { id: 1,
title: 'First issue', author: {id: 1}, assignee: {id: 1} } ] export default function issues(state, action) { if (typeof state == 'undefined') { return initialState } switch (action.type) { case ADD: let issue = action.issue return [ ...state, { id: state.length + 1, title: issue.title, description: issue.description } ] case REFRESH: return action.issues } return state }
Container • ಛผͳComponent • ReduxͱReactΛܨ͛Δଘࡏ • ҎԼͷͷΛpropertyͱͯ͠ड͚औΕΔΑ͏ʹͳΔ • ReducerʹΑͬͯ࡞ΒΕΔঢ়ଶ(store) •
ActionΛൃߦ͢ΔͨΊͷdispatchؔ
Container import React, { Component } from 'react' import {
bindActionCreators } from 'redux' import { addIssue, loadIssues } from '../actions/issues' class App extends Component { onAdd(issue) { this.props.dispatch(addIssue(issue)) } render() { const { dispatch, issues } = this.props return ( <div> <IssueList issues={issues} onAdd={this.onAdd.bind(this)} /> </div> ) } } function mapStateToProps(state) { return { issues: state.issues } } export default connect(mapStateToProps)(App)
Container issuesͱdispatch͕͞Ε͍ͯΔ
Fluxͯ͠ΈΔ • git checkout flux • saveͯ͠Ճ͞Εͳ͍ϑΥʔϜ͕͋Δ • TODO •
reducers/issues.js • containers/App.js
reducers/issues.js • ΞΫγϣϯ͕ൃߦ͞Εͨͱ͖ʹstate͕Ͳ͏ม ΘΔ͔ͷ͕ؔ͋Δ͚ͩ import { ADD, REFRESH } from
'../actions/issues' const initialState = [ { id: 1, title: 'First issue', author: {id: 1}, assignee: {id: 1} } ] export default function issues(state, action) { if (typeof state == 'undefined') { return initialState } switch (action.type) { case ADD: //TODO: Add new issue } return state }
reducers/issues.js • ADDΞΫγϣϯͰɺstateʹissueΛՃͨ͠৽ ͍͠stateΛฦ͍͍ͤ import { ADD, REFRESH } from
'../actions/issues' const initialState = [ { id: 1, title: 'First issue', author: {id: 1}, assignee: {id: 1} } ] export default function issues(state, action) { if (typeof state == 'undefined') { return initialState } switch (action.type) { case ADD: //TODO: Add new issue } return state }
reducers/issues.js • ৽͍͠ྻΛฦ͢ • stateʹೖ͍͚ͯ͠ͳ͍ case ADD: let issue =
action.issue return [ ...state, { id: state.length + 1, title: issue.title, description: issue.description } ]
containers/App.js • ϑΥʔϜͷίʔϧόοΫͰɺActionΛൃߦ͠ ͍ͨ import React, { Component } from
'react' import { connect } from 'react-redux' import IssueList from '../components/IssueList' import { addIssue } from '../actions/issues' class App extends Component { onAdd(issue) { //TODO: Dispatch addIssue action! } render() { return ( <div> <IssueList issues={this.props.issues} onAdd={this.onAdd.bind(this)} /> </div> ) } }
containers/App.js • addIssueؔͰฦ͞ΕΔΞΫγϣϯΛɺ this.props.dispatchʹ͢ import IssueList from '../components/IssueList' import {
addIssue } from '../actions/issues' class App extends Component { onAdd(issue) { this.props.dispatch(addIssue(issue)) } render() { return ( <div> <IssueList issues={this.props.issues} onAdd={this.onAdd.bind(this)} /> </div> ) } }
Ajax • αʔόʔ͔ΒσʔλΛऔಘ͍ͨ͠ • ActionͰΔͷ͕Ұൠత • redux-thunk ͱ͍͏middlewareΛ͏ • git
checkout ajax
Ajax • ActionCreatorͰdispatchΛड͚ΔؔΛฦ͢ export const ADD = 'ADD_ISSUE' export const
LOAD = 'LOAD_ISSUES' export const REFRESH = 'REFRESH_ISSUES' export function addIssue(issue) { return { type: ADD, issue: issue } } export function refreshIssues(issues) { return { type: REFRESH, issues: issues } } export function loadIssues() { return dispatch => { fetch('/api/issues') .then(res => res.json()) .then(json => { dispatch(refreshIssues(json)) }) } }
Ajax • ड͚औͬͨdispatchΛඇಉظͰ࣮ߦ͢Δ export const ADD = 'ADD_ISSUE' export const
LOAD = 'LOAD_ISSUES' export const REFRESH = 'REFRESH_ISSUES' export function addIssue(issue) { return { type: ADD, issue: issue } } export function refreshIssues(issues) { return { type: REFRESH, issues: issues } } export function loadIssues() { return dispatch => { fetch('/api/issues') .then(res => res.json()) .then(json => { dispatch(refreshIssues(json)) }) } }
Ajax • Reducer import { ADD, REFRESH } from '../actions/issues'
export default function issues(state, action) { if (typeof state == 'undefined') { return initialState } switch (action.type) { case ADD: let issue = action.issue return [ ...state, { id: state.length + 1, title: issue.title, description: issue.description } ] case REFRESH: return action.issues } return state }
αʔόʔͰϨϯμϦϯάͯ͠ ΈΔ
αʔόʔαΠυϨϯμϦϯά • SEOͷ؍ • Ϋϩʔϥʔ͕Ͳ͜·ͰJavaScriptΛ্ख͘ѻ͍͑ͯΔ͔ෆ҆ • ੩తͳϖʔδΛฦ͍ͨ͠ • Ϣʔβʔମݧ •
ϖʔδʹΞΫηε͔ͯ͠ΒɺReactͷ࣮ߦΛͬͯɺAPIϦ ΫΤετૹͬͯɺඳը͞ΕΔͷ͕ɺ͍
Έ • DOM৮ͬͯͳ͍ͷͰɺαʔόʔଆͰ࣮ߦͰ͖ΔJS ʹͳ͍ͬͯΔ • ΞϓϦέʔγϣϯͷঢ়ଶ͕ܾ·ΕɺϏϡʔܾ· Δ • DOMͱͯ͠Ͱͳ͘ɺจࣈྻͱͯ͠Ϩϯμʔ͢Δ •
ͦͷঢ়ଶͷΦϒδΣΫτΛಉ࣌ʹฦ͢
ͬͯΈΔ • git checkout ssr • npm start ͷ࠶ىಈ
server.js • client.jsͱେମಉ͡ • renderToStringͰHTMLจࣈ ྻ͕࡞ΒΕΔ //TODO: Interpolate html and
initialState function renderFullPage(html, initialState) { return … } function handler(req, res) { const store = createStore(issueApp) //TOOD: Set initial data const html = renderToString( <Provider store={store}> <App /> </Provider> ) const initialState = store.getState() res.send(renderFullPage(html, initialState)) } app.use(Express.static('static')) app.use('/api/issues', issuesHandler) app.use(handler) app.listen(port)
server.js • renderFullPageͰrenderToString͞ΕͨHTML ͱɺॳظstateΛςϯϓϨʔτʹૠೖ͢Δ͚ͩ //TODO: Interpolate html and initialState function
renderFullPage(html, initialState) { return ` <!doctype html> <html> <head> <title>React Sokushu</title> </head> <body> <div id="root"></div> <script src="/dist/bundle.js"></script> </body> </html> ` }
server.js • renderFullPageͰrenderToString͞ΕͨHTML ͱɺॳظstateΛςϯϓϨʔτʹૠೖ͢Δ͚ͩ function renderFullPage(html, initialState) { return `
<!doctype html> <html> <head> <title>React Sokushu</title> </head> <body> <div id="root">${html}</div> <script> window.__INITIAL_STATE__ = ${JSON.stringify(initialState)} </script> <script src="/dist/bundle.js"></script> </body> </html> ` }
view-source
client.js • ঢ়ଶ͕Viewͱҧ͏ͱαΠϨϯμʔ͞ΕΔͷ Ͱɺॳظstate͔ΒstoreΛͭ͘ΔΑ͏ʹ͢Δ const store = createStore( issueApp, window.__INITIAL_STATE__,
applyMiddleware(thunk) ) render( <Provider store={store}> <App /> </Provider>, document.getElementById("root") )
CSS in React
CSS in React • CSSͷ • ໊લͷিಥ • Ϋϥε໊Λ͍ͬͺ͍ߟ͑ͳ͍ͱ •
ΘΕͳ͘ͳͬͨίʔυ͕ফͤͳ͍ • ංେԽ͢Δcss • JSXͰϏϡʔΛ.jsͰॻ͘Α͏ʹͳͬͨͷͰɺελΠϧҰॹʹॻ͖ ͍ͨ
CSS in React • ΫϥεΛ͚ΔΘΓʹinlineͰॻ͘ • style={this.styles.base} Έ͍ͨʹ • ܧঝͱ͔ਏ͍
• :hoverͱ͔͔͚ͳ͍
Radium • coreͰΓͳ͍ػೳΛՃ • https://github.com/FormidableLabs/radium • :hoverͰ͖Δ • ܧঝͰ͖Δ
ॻ͍ͯΈΔ • git checkout css • IssueForm͕ͪΐͬͱ͚ͩ៉ྷʹ
components/IssueForm.js import React, { Component } from 'react' import Radium
from 'radium' import color from 'color' class IssueForm extends Component { render() { return ( <div> <form onSubmit={this.onSubmit.bind(this)}> <fieldset> <legend>Create Issue</legend> <input style={styles.input} type="text" value={this.state.title} …/> <textarea style={styles.input} value={this.state.description} … /> <button style={styles.button}>Save</button> </fieldset> </form> </div> ) } } const styles = { input: { display: 'block', width: '200px', padding: '6px 10px', margin: '10px 0', border: '1px solid #ccc' }, button: { backgroundColor: '#00A4BB', border: 'none', padding: '6px 15px', color: 'white', cursor: 'pointer', } } export default Radium(IssueForm)
components/IssueForm.js class IssueForm extends Component { render() { return (
<div> <form onSubmit={this.onSubmit.bind(this)}> <fieldset> <legend>Create Issue</legend> <input style={styles.input} type="text" value={this.state.title} …/> <textarea style={styles.input} value={this.state.description} … /> <button style={styles.button}>Save</button> </fieldset> </form> </div> ) } } inlineͰ͍ͯͯ͘ దͳΫϥε໊ߟ͑ΔΑΓ؆୯
components/IssueForm.js const styles = { input: { display: 'block', width:
'200px', padding: '6px 10px', margin: '10px 0', border: '1px solid #ccc' }, button: { backgroundColor: '#00A4BB', border: 'none', padding: '6px 15px', color: 'white', cursor: 'pointer', } } ΦϒδΣΫτͰهड़
ͬͯΈΔ • :hoverͰϘλϯͷ৭Λม͑Δ • textareaͷߴ͞Λม͑Δ
components/IssueForm.js import React, { Component } from 'react' import Radium
from 'radium' import color from 'color' const styles = { input: { display: 'block', width: '200px', padding: '6px 10px', margin: '10px 0', border: '1px solid #ccc' }, textarea: { height: '100px' }, button: { backgroundColor: '#00A4BB', border: 'none', padding: '6px 15px', color: 'white', cursor: 'pointer', ':hover': { backgroundColor: color('#00A4BB').darken(0.2).hexString(), } } }
components/IssueForm.js • mouseenter, mouseleaveͰؤு͚ͬͯସ͑ ͯ͘Δ const styles = { button:
{ backgroundColor: '#00A4BB', border: 'none', padding: '6px 15px', color: 'white', cursor: 'pointer', ':hover': { backgroundColor: color('#00A4BB').darken(0.2).hexString(), } } }
components/IssueForm.js • ྻͰࢦఆ͢Δͱɺoverrideͯ͘͠ΕΔ • style={[styles.input, styles.textarea]} class IssueForm extends Component
{ render() { return ( <div> <form onSubmit={this.onSubmit.bind(this)}> <fieldset> <legend>Create Issue</legend> <input style={[styles.input]} type="text" value={this.state.title} … /> <textarea style={[styles.input, styles.textarea]} value={this.state.description} … /> <button style={[styles.button]}>Save</button> </fieldset> </form> </div> ) } }
components/IssueForm.js • ྻͰࢦఆ͢Δͱɺoverrideͯ͘͠ΕΔ • style={[styles.input, styles.textarea]} class IssueForm extends Component
{ render() { return ( <div> <form onSubmit={this.onSubmit.bind(this)}> <fieldset> <legend>Create Issue</legend> <input style={[styles.input]} type="text" value={this.state.title} … /> <textarea style={[styles.input, styles.textarea]} value={this.state.description} … /> <button style={[styles.button]}>Save</button> </fieldset> </form> </div> ) } }
·ͱΊ • ΫϥΠΞϯταΠυͷॻ͖ํ͕େ͖͘มΘΔ • ϏϡʔCSSjsʹೖΕΔ • ίϯϙʔωϯτͷڍಈ͚ͦͩ͜ΈΕશ ͔ͯΔʂ • αʔόʔαΠυϨϯμϦϯά؆୯