Slide 1

Slide 1 text

React/Redux Introduction 2016/7/8

Slide 2

Slide 2 text

ࣗݾ঺հ ੢ాխത 2015/05ʹBizreachʹస৬ɺWebܦݧ1೥ ٻਓݕࡧΤϯδϯελϯόΠͷࣾ಺޲͚؅ཧ ը໘ΛReact/ReduxͰ։ൃத https://github.com/adwd https://twitter.com/adwd118

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

ελϯόΠ؅ཧը໘೥ද 2015/5 ~ 2015/5 jQuery 2015/7 AngularJS 1 2015/12 React/Redux

Slide 6

Slide 6 text

΍Βͳ͍͜ͱ • React/Reduxͷৄ͍͠આ໌ • React vs Angular, facebook/flux vs redux ..

Slide 7

Slide 7 text

΍Δ͜ͱ • React/ReduxͰTodoΞϓϦҎ্ͷ΋ͷΛ࡞Δ ͨΊʹඞཁͳཁૉͷβοͱͨ͠આ໌ • React, Redux, react-redux, react-router • TodoΞϓϦΛ࡞ΕΔʢؾ͕͢ΔʣΑ͏ʹͳΔ ͷ͕ΰʔϧ

Slide 8

Slide 8 text

Ξϯέʔτ • ϓϩάϥϛϯάɾjavascriptະܦݧ • ϓϩάϥϛϯάɾjavascriptνϣοτϫΧϧʢॳ৺ऀʣ • React/ReduxͷνϡʔτϦΞϧΛ΍ͬͨʢ΍Ζ͏ͱͨ͠ʣ • React/ReduxνϣοτσΩϧʢීஈ͔Β࢖ͬͯΔʣ

Slide 9

Slide 9 text

Ξϯέʔτ̎ • ඇΤϯδχΞ • σβΠφʔ • ϑϩϯτΤϯυΤϯδχΞ • ϞόΠϧΞϓϦΤϯδχΞ • ͦΕҎ֎ʢόοΫΤϯυͱ͔Πϯϑϥͱ͔ʣ

Slide 10

Slide 10 text

ίϯςϯπ • ଎शES2015+JSX • React • Redux • ඇಉظॲཧ, redux-thunk • React-router • ςετ

Slide 11

Slide 11 text

modules default import/export // MyApp.js import foo from './MyModule' // MyModule.js export default function foo () { ... }

Slide 12

Slide 12 text

modules named import/export // MyApp.js import { foo, Bar, baz } from './MyModule' // MyModule.js export function foo () { ... } export class Bar { ... } export const baz = ... ࢀর: http://www.slideshare.net/teppeis/you-dont-know-es-modules

Slide 13

Slide 13 text

class import MyModule, { myAdd, MyBaseClass } from './MyModule' class MyClass extends MyBaseClass { constructor() { } add = (a, b) => { return myAdd(a, b) } show = ({ a, b }) => { const { foo, bar } = a const { baz: { box = 1 }} = b console.log(MyModule.show({ foo, bar, box })) } } ※babel plugin transform-class-properties͕ඞཁ

Slide 14

Slide 14 text

destructuring assignment import MyModule, { myAdd, MyBaseClass } from './MyModule' class MyClass extends MyBaseClass { constructor() { } add = (a, b) => { return myAdd(a, b) } show = ({ a, b }) => { const { foo, bar } = a const { baz: { box = 1 }} = b console.log(MyModule.show({ foo, bar, box })) } } show = (obj) => { const a = obj.a const b = obj.b const foo = a.foo const bar = a.bar const box = b.baz.box || 1 ... } 㱻

Slide 15

Slide 15 text

https://facebook.github.io/react/html-jsx.html JSX class NewComponent extends React.Component { render() { return (
{/* Hello world */}
Enter your name:

Enter your HTML here

); } }
Enter your name:

Enter your HTML here

Slide 16

Slide 16 text

JSX class NewComponent extends React.Component { render() { const todos = ['eat', 'sleep'] return (
    { todos.map(item =>
  • {item}
  • ) }
); } }
  • eat
  • sleep

Slide 17

Slide 17 text

spread operator const list = [1, 2, 3] const list2 = [ ...list, 4] // [1, 2, 3, 4] const obj = { a: 'foo', b: 1 } const obj2 = { ...obj, b: 2, c: 'bar' } // { a: 'foo', b: 2, c: 'bar' } const prop = { className: 'my-class', style: { color: ‘white’ } } const div = (

foo bar

) //

Slide 18

Slide 18 text

αϯϓϧϓϩδΣΫτ

Slide 19

Slide 19 text

ηοτΞοϓ // Node.js 5.0Ҏ߱ΛΠϯετʔϧ cd your/working/directory git clone https://github.com/adwd/react-redux-hands-on npm install npm run dev open http://localhost:3000ɹ// ͜Μͳը໘͕දࣔ͞ΕͨΒOK!

Slide 20

Slide 20 text

αϯϓϧϓϩδΣΫτ • 3ͭͷTodoΞϓϦ • ReactͷΈ • React/Redux • React/Redux + REST API 5PEPͷδϨϯϚ w 3FEVYͰෳࡶͳঢ়ଶΛ؅ཧͰ͖Δˠαϯϓϧ ʹͦΜͳෳࡶͳͷΛग़ͤͳ͍ˠ݁ہ5PEP

Slide 21

Slide 21 text

αϯϓϧϓϩδΣΫτ • React, Redux, React-router, Babel, ESLint, Webpackͳ Ͳ͕͍͍ײ͡ʹઃఆ͞ΕͨϘΠϥʔϓϨʔτ͔Βfork • https://github.com/davezuko/react-redux-starter-kit • มߋ఺: • react/reduxαϯϓϧίʔυͷ௥Ճɺmaterial-uiͷ௥ ՃͳͲ • forkݩ͸େن໛ͳॻ͖׵͕͑ਐΈશવҧͬͯ·͢

Slide 22

Slide 22 text

αϯϓϧϓϩδΣΫτߏ੒ • src • layouts - ΞϓϦέʔγϣϯશମͰදࣔͯ͠ ͍Δόʔɺυϩϫʔίϯϙʔωϯτ • redux - reduxͷAction, Reducer • routes - react-routerͷϧʔςΟϯά • views - React/Reduxίϯϙʔωϯτ • server/api - json api

Slide 23

Slide 23 text

Redux DevTools • Reduxͷ։ൃʹศརͳChrome֦ு • https://chrome.google.com/webstore/ detail/redux-devtools/ lmhkpmbekcpmknklioeibfkpmmfibljd

Slide 24

Slide 24 text

͜͜Ͱը໘Λ͞ΘͬͯΈΔ

Slide 25

Slide 25 text

ESLintͷεεϝ • javascriptͷจ๏ΛνΣοΫͯ͘͠ΕΔ • ΤσΟλͱ࿈ܞ͢Δͷ͕Φεεϝ • eslint —fix Ͱ͋Δఔ౓ͷमਖ਼͸΍ͬͯ͘ΕΔ • νʔϜ։ൃͰίʔυϨϏϡʔָ͕ʹͳΔ

Slide 26

Slide 26 text

ESLintͷεεϝ • ྫɿ Atom + linter-eslint

Slide 27

Slide 27 text

React

Slide 28

Slide 28 text

React • ViewϥΠϒϥϦ • js-centric: શ෦jsͷதʹॻ͘ • ίϯϙʔωϯτࢦ޲

Slide 29

Slide 29 text

React࠷খߏ੒ // index.html
</body> </html> // sample.js import React from 'react' import ReactDOM from 'react-dom' import MyComponent from './MyComponent' class Example extends React.Component { render () { return ( <div> <h1>Hello, world!</h1> <MyComponent message='Hello, world!' /> </div> ) } } ReactDOM.render( <Example />, document.getElementById('example') )

Slide 30

Slide 30 text

React࠷খߏ੒ // index.html
</body> </html> // sample.js import React from 'react' import ReactDOM from 'react-dom' import MyComponent from './MyComponent' class Example extends React.Component { render () { return ( <div> <h1>Hello, world!</h1> <MyComponent message='Hello, world!' /> </div> ) } } ReactDOM.render( <Example />, document.getElementById('example') ) React.Component

Slide 31

Slide 31 text

React ίϯϙʔωϯτ import React from 'react' class Sample extends React.Component { constructor (props) { super(props) this.state = { text: props.prefix + 'learn react' } } render () { return (

{this.state.text}

) } }

Slide 32

Slide 32 text

React ίϯϙʔωϯτ import React from 'react' class Sample extends React.Component { constructor (props) { super(props) this.state = { text: props.prefix + 'learn react' } } render () { return (

{this.state.text}

) } } React.ComponentΛܧঝ͠ɺ renderϝιουΛ࣋ͭΫϥε

Slide 33

Slide 33 text

React ίϯϙʔωϯτ import React from 'react' class Sample extends React.Component { constructor (props) { super(props) this.state = { text: props.prefix + 'learn react' } } render () { return (

{this.state.text}

) } } ֎෦͔ΒpropsΛड͚औΔ ಺෦ʹstateΛ࣋ͭ

Slide 34

Slide 34 text

import React from 'react' class MyReactComponent extends React.Component { render () { return (

This is My Component

{this.props.children}
) } }

learn react

learn flux

props.children

Slide 35

Slide 35 text

import React from 'react' class MyReactComponent extends React.Component { render () { return (

This is My Component

{this.props.children}
) } }

learn react

learn flux

props.children Reactίϯϙʔωϯτʹωετͨ͠ ཁૉ͸props.childrenʹೖΔ

Slide 36

Slide 36 text

React sample • localhost:3000/react • src/views/ReactSample/TodoList.js • src/views/ReactSample/TodoItem.js

Slide 37

Slide 37 text

αϯϓϧΛ͞ΘͬͯΈΔ

Slide 38

Slide 38 text

Components • TodoList • TodoΛstateͱͯ͠؅ཧ • TodoItem • TodoΛҰͭදࣔ • Todo࡟আϘλϯΛදࣔ 5PEP-JTU 5PEP*UFN ˣ5PEP-JTUίϯϙʔωϯτͷ TUBUFΛͦͷ··දࣔ

Slide 39

Slide 39 text

TodoList state { newTodo: '', todos: [ 'learn react’, 'learn flux’, 'learn redux’ ] } • ௚઀DOMͷૢ࡞͸͠ͳ͍ɺ ঢ়ଶΛૢ࡞ͯ͠React͕ͦΕ Λඳը͢Δ

Slide 40

Slide 40 text

TodoList render() render () { return (

React Sample

Todos

add todo { this.state.todos.map((text, index) => ( {text} )) } …
) }

Slide 41

Slide 41 text

TodoList render() render () { return (

React Sample

Todos

add todo { this.state.todos.map((text, index) => ( {text} )) } …
) }

Slide 42

Slide 42 text

import React, { Component } from 'react' export default class TodoList extends Component { constructor (props) { super(props) this.state = { newTodo: '', todos: [ 'learn react’, 'learn flux’, 'learn redux' ] } } ... } TodoList ίϯετϥΫλ

Slide 43

Slide 43 text

import React, { Component } from 'react' export default class TodoList extends Component { constructor (props) { super(props) this.state = { newTodo: '', todos: [ 'learn react’, 'learn flux’, 'learn redux' ] } } ... } TodoList ίϯετϥΫλ ঢ়ଶͷॳظԽ

Slide 44

Slide 44 text

TodoList ΫϥεϓϩύςΟ changeText = (e) => { this.setState({ newTodo: e.target.value }) } addTodo = () => { const newTodo = this.state.newTodo this.setState({ newTodo: '', todos: [...this.state.todos, newTodo] }) } deleteTodoItem = (index) => { this.setState({ todos: this.state.todos.filter((todo, i) => i !== index) }) }

Slide 45

Slide 45 text

changeText = (e) => { this.setState({ newTodo: e.target.value }) } addTodo = () => { const newTodo = this.state.newTodo this.setState({ newTodo: '', todos: [...this.state.todos, newTodo] }) } deleteTodoItem = (index) => { this.setState({ todos: this.state.todos.filter((todo, i) => i !== index) }) } TodoList ΫϥεϓϩύςΟ this.setState stateͷࢦఆͨ͠ύε ͚ͩߋ৽͢Δ

Slide 46

Slide 46 text

TodoItem.js import React, { Component, PropTypes } from 'react' export default class TodoItem extends Component { static propTypes = { index: PropTypes.number, onRemove: PropTypes.func, children: PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.node), React.PropTypes.node ]) } render() { const { index, onRemove, children } = this.props; return (
{`${index + 1}`}: {children} onRemove(index)} />
); } }

Slide 47

Slide 47 text

TodoItem.js import React, { Component, PropTypes } from 'react' export default class TodoItem extends Component { static propTypes = { index: PropTypes.number, onRemove: PropTypes.func, children: PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.node), React.PropTypes.node ]) } render() { const { index, onRemove, children } = this.props; return (
{`${index + 1}`}: {children} onRemove(index)} />
); } } render()

Slide 48

Slide 48 text

TodoItem.js import React, { Component, PropTypes } from 'react' export default class TodoItem extends Component { static propTypes = { index: PropTypes.number, onRemove: PropTypes.func, children: PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.node), React.PropTypes.node ]) } render() { const { index, onRemove, children } = this.props; return (
{`${index + 1}`}: {children} onRemove(index)} />
); } } propTypes: ࣮ߦ࣌ܕνΣοΫ

Slide 49

Slide 49 text

ίʔυΛ͍ͬͯ͡ΈΔ • jsϑΝΠϧΛมߋɾอଘ͢ΔͱࣗಈͰ൓ө͞ΕΔ͸ͣ • Webpack Hot Reloading • localhost:3000/react • src/views/ReactSample/TodoList.js • src/views/ReactSample/TodoItem.js

Slide 50

Slide 50 text

࣮ࡍʹ͍ͬͯ͡ΈΔ

Slide 51

Slide 51 text

React ·ͱΊ • ViewϥΠϒϥϦ • ίϯϙʔωϯτ • props, props.children • this.state, this.setState

Slide 52

Slide 52 text

React ·ͱΊ • ViewϥΠϒϥϦ • ͳͷͰMVCͰ͍͏Model, Controllerʹ͋ͨΔػೳ͕ͳ͍ • this.state, this.setStateͰෳࡶͳঢ়ଶΛ؅ཧ͢Δͷ͸… → Redux • ࣭໰ͱ͔ʁ

Slide 53

Slide 53 text

Redux

Slide 54

Slide 54 text

Redux • FluxΞʔΩςΫνϟͷ࣮૷ͷҰͭ • Flux࣮૷͸facebook/flux͔reduxͷͲͪΒ͔ͱ͍͏ งғؾʁ • υΩϡϝϯτ͕ॆ࣮ http://redux.js.org • ࣮૷͕300ߦ͔͠ͳ͍ͷͰಡΉͷ΋ΞϦ • ΞϓϦέʔγϣϯͷঢ়ଶΛҰͭͷJSONͰදݱ͢Δ

Slide 55

Slide 55 text

୯ํ޲σʔλϑϩʔ IUUQXXXTMJEFTIBSFOFU+POBT0IMTTPOVTJOHSFEVY

Slide 56

Slide 56 text

୯ํ޲σʔλϑϩʔ • View͸Storeͷঢ়ଶΛඳը͢Δ͚ͩ • ঢ়ଶΛมߋ͢Δͷ͸Action͚ͩ • Reducer͸(currentState, action) => nextStateͳ ͨͩͷؔ਺

Slide 57

Slide 57 text

Action • ΞϓϦέʔγϣϯͷঢ়ଶΛมԽͤ͞ΔΑ͏ ͳಈ࡞Λදݱͨ͠plain javascript object • ex) Todoͷ௥ՃɺAPIΞΫηεͷཁٻ/੒ޭ • typeϑΟʔϧυ͕ඞཁ { "type": "ADD_TODO", "text": "Build my first Redux app" } { "type": "FETCH_USER_SUCCEEDED", "id": 1, "name": "Dan Abramov", }

Slide 58

Slide 58 text

Reducer • ࣮ߦ͞Εͨactionͱݱࡏͷstate͔Βɺ࣍ͷ stateΛܭࢉ͢Δ • ͨͩͷؔ਺ • (currentState, action) => nextState • action + reducer === setState తͳʁ

Slide 59

Slide 59 text

Reducer const initalState = [] function todo(state = initalState, action) { switch (action.type) { case "ADD_TODO": return [...state, action.text] case "DELETE_TODO": return state.filter( (text, index) => index !== action.index ) default: return state } } state: [], action: { type: “ADD_TODO”, text: “hi” } ! nextState = reducer(state, action) ! nextState: [ “hi” ]

Slide 60

Slide 60 text

Store • ΞϓϦέʔγϣϯͷঢ়ଶΛอ࣋͢Δ • Store͸ΞϓϦέʔγϣϯதʹͻͱͭ • ΞϓϦέʔγϣϯͷঢ়ଶ͸1ຕͷJsonʹͳΔ

Slide 61

Slide 61 text

Store API • createStore: (reducer) => Store • store.dispatch: ΞΫγϣϯͷ࣮ߦ import { createStore } from 'redux' import todo from './TodoReducer' const store = createStore(todo) store.dispatch({type: 'ADD_TODO', text: 'learn react'}) store.subscribe(() => { console.log(store.getState()) // [ ‘learn react’ ] })

Slide 62

Slide 62 text

View • ͳΜͰ΋Α͍ • console.log, vue.js, Angular 1/2 … • Reactͷ৔߹͸ɺReactͱReduxͷ࿈ܞʹ react-reduxΛ࢖͏

Slide 63

Slide 63 text

react-redux

Slide 64

Slide 64 text

react-redux • ReactͱReduxΛͭͳ͙ • ReduxͷStoreͱReactͷprops • ReduxͷΞΫγϣϯͱReactͷΠϕϯτ

Slide 65

Slide 65 text

react-redux • ReactͱReduxΛͭͳ͙ • ReactίϯϙʔωϯτͷpropsʹɺRedux StoreͷstateͱdispatchΛInject͢Δ

Slide 66

Slide 66 text

react-redux class TodoList extends React.Component { render() {
{ this.props.todo.map((text, index) => { {text} }) }
} } store.dispatch store.getState().todo

Slide 67

Slide 67 text

react-redux • API͸2͚ͭͩ • connect • Provider

Slide 68

Slide 68 text

react-redux: connect • Redux Storeͷstate͔Βඞཁͳ෦෼ΛReact ίϯϙʔωϯτͷpropsʹ஫ೖ͢ΔηϨΫλ

Slide 69

Slide 69 text

react-redux: connect import { connect } from 'react-redux' class TodoList extends React.Component { render() {
{ this.props.todo.map((text, index) => { {text} }) }
} } const connectedTodoList = connect( state => { todo: state.todo } )(TodoList) export default connectedTodoList { "todo": "...", "users": "...", "foo": "..." }

Slide 70

Slide 70 text

react-redux: connect import { connect } from 'react-redux' class TodoList extends React.Component { render() {
{ this.props.todo.map((text, index) => { {text} }) }
} } const connectedTodoList = connect( state => { todo: state.todo } )(TodoList) export default connectedTodoList store.dispatch store.getState().todo ঢ়ଶΛද͢Jsonશମ͔Βtodo ෦෼͚ͩΛTodoListʹInject

Slide 71

Slide 71 text

react-redux: Provider • connectΛݺ΂ΔΑ͏ʹ͢Δ • ReactΞϓϦέʔγϣϯͷϧʔτίϯϙʔ ωϯτΛProviderͰϥοϓ

Slide 72

Slide 72 text

react-redux: Provider import React from 'react' import { render } from 'react-dom' import App from './components/App' render( , document.getElementById('root') ) import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' import { createStore } from 'redux' import todo from './reducers' import App from './components/App' const store = createStore(todo) render( , document.getElementById('root') )

Slide 73

Slide 73 text

Reduxαϯϓϧ • http://localhost:3000/redux • src/views/ReduxSample/ReduxSample.js • src/redux/modules/todo.js

Slide 74

Slide 74 text

Redux DevTools • ActionͱStateΛ࣌ܥྻͰදࣔ • աڈͷStateʹߦͬͨΓͰ͖Δ

Slide 75

Slide 75 text

αϯϓϧΛ͞ΘͬͯΈΔ

Slide 76

Slide 76 text

Action (ActionCreator) // src/redux/modules/todo.js export function addTodo () { return { type: 'ADD_TODO' } } export function removeTodo (index) { return { type: 'REMOVE_TODO', index } } export function editNewTodo (text) { return { type: 'EDIT_NEW_TODO', text } } • ͦ͜Β͡Ύ͏ʹ dispatch({ type: ‘ADD_TODO’ })Λॻ͔ͳ ͍Α͏ʹؔ਺ʹ͓ͯ͘͠ • dispatch(addTodo())

Slide 77

Slide 77 text

// src/redux/modules/todo.js // Initial state const initialState = { newTodo: '', todos: [ 'learn react', 'learn flux', 'learn redux' ] } // Reducer export default function todoReducer (state = initialState, action) { switch (action.type) { case 'ADD_TODO': return { newTodo: '', todos: [...state.todos, state.newTodo] } case 'REMOVE_TODO': return { ...state, todos: state.todos.filter((todo, i) => i !== action.index) } case 'EDIT_NEW_TODO': return { ...state, newTodo: action.text } default: return state } } Reducer

Slide 78

Slide 78 text

Component // src/views/ReduxSample/ReduxSample.js import { addTodo, removeTodo, editNewTodo } from ‘../../redux/modules/todo’ class ReduxSample extends Component { render () { const { todo: { newTodo, todos }, dispatch } = this.props return (
dispatch(editNewTodo(e.target.value)} /> ...
) } } const ConnectedReduxSample = connect( state => ({todo: state.todo}) )(ReduxSample) export default ConnectedReduxSample

Slide 79

Slide 79 text

Redux, react-redux ·ͱΊ • Redux • Flux, ୯ํ޲σʔλϑϩʔ • Action, Reducer, Store, View • react-redux • connectͰReduxͷঢ়ଶΛpropsʹinject • ProviderͰϧʔτͷίϯϙʔωϯτΛϥοϓ • ࣭໰ͳͲʁ

Slide 80

Slide 80 text

͜͜·Ͱͷ·ͱΊ • React • Reduxɺreact-redux • TodoΞϓϦΛ࡞Δ͜ͱ͕Ͱ͖ͦ͏

Slide 81

Slide 81 text

͔͜͜Β • ඇಉظॲཧ • Web APIΛୟ͘Α͏ͳॲཧ • ϧʔςΟϯά • /users/1 • ςετ

Slide 82

Slide 82 text

ඇಉظॲཧ

Slide 83

Slide 83 text

ඇಉظαϯϓϧ • http://localhost:3000/redux-async • src/views/ReduxAsyncSample/ • src/modules/asyncTodo/asyncTodo.js

Slide 84

Slide 84 text

ඇಉظαϯϓϧ server: localhost:3000/api/ • GET todos • POST todos/add • DELETE todos/:index source: server/api/todos.js 2 seconds delay

Slide 85

Slide 85 text

αϯϓϧΛ͞ΘͬͯΈΔ

Slide 86

Slide 86 text

ඇಉظॲཧ • ReduxͷActionʹඇಉظॲཧΛͷ͍ͤͨ • redux-thunkΛ࢖͏ • redux-thunk: redux middlewareͷͻͱͭͰɺ promiseΛฦؔ͢਺ΛActionʹ౉ͤΔ • redux middleware: reducer࣮ߦ࣌ʹ͍͍ײ ͡ʹͯ͘͠ΕΔ΍ͭ

Slide 87

Slide 87 text

Async Action • API΁ϦεΤετૹ৴ɾϨεϙϯεड৴ͷ2 ౓ΞΫγϣϯΛ࣮ߦ͢Δ 2 seconds after request action response action

Slide 88

Slide 88 text

Async Action ඇಉظΞΫγϣϯ = dispatch => promiseͳؔ਺ // action creator const request = () => { type: 'REQUESTING' } const receive = (data) => { type: 'RECEIVED', data } // async action creator export function apiAccess() { return dispatch => { dispatch(request()) axios.get('api/sample') .then(response => { dispatch(receive(response.data)) }) .catch(e => { // error }) } }

Slide 89

Slide 89 text

// action creator const request = () => { type: 'REQUESTING' } const receive = (data) => { type: 'RECEIVED', data } // async action creator export function apiAccess() { return dispatch => { dispatch(request()) axios.get('api/sample') .then(response => { dispatch(receive(response.data)) }) .catch(e => { // error }) } } Async Action ϦΫΤετૹ৴લʹ requestΞΫγϣϯ ϨεϙϯεΛड৴ͨ͠ͱ͜ΖͰ receiveΞΫγϣϯ

Slide 90

Slide 90 text

Async Action (Reducer) const initialState = { loading: false, todos = [] } function asyncReducer(state = initialState, action) { switch (action.type) { case 'REQUESTING': return { loading: true, todos = [] } case 'RECEIVED': return { loading: false, todos = action.data } default: return state } }

Slide 91

Slide 91 text

Async Action (Component) class AsyncComponent extends React.Component { componentDidMount() { this.props.dispatch(fetchTodos()) } render () {
{ this.props.todo.loading ?

loading...

: }
} } export default connect(state => { todo: state.todo })(AsyncComponent)

Slide 92

Slide 92 text

Async Action (Component) class AsyncComponent extends React.Component { componentDidMount() { this.props.dispatch(fetchTodos()) } render () {
{ this.props.todo.loading ?

loading...

: }
} } export default connect(state => { todo: state.todo })(AsyncComponent) loadingͰදࣔΛ੾Γସ͑ componentDidMountͰ APIʹϦΫΤετΛૹ৴

Slide 93

Slide 93 text

ඇಉظॲཧ·ͱΊ • redux-thunk • ࠷ۙ͸redux-saga͕ਓؾΒ͍͠(?) • Netflix੡ͷredux-observableͱ͔΋ • ඇಉظɾ෭࡞༻Λѻ͏Redux middlewareͷઓࠃ࣌୅ײ • ΞΫηε։࢝ɾ׬ྃͰ2ճΞΫγϣϯΛ࣮ߦ • Storeͷঢ়ଶʹAPIΞΫηεঢ়ଶΛஔ͍͓ͯ͘ • ࣭໰ͳͲʁ

Slide 94

Slide 94 text

react-router

Slide 95

Slide 95 text

react-router • URLભҠͱReactίϯϙʔωϯτදࣔΛಉظ ͢Δ • src/routes/index.js

Slide 96

Slide 96 text

ओͳAPI • , • ϧʔςΟϯάΛߦ͏ • • • ͷ໾ׂ • show users • withRouter • ReactίϯϙʔωϯτͷpropsʹrouterػೳΛInject • withRouter(MyComponent)

Slide 97

Slide 97 text

routing // src/routes/index.js import React from 'react' import { Route, IndexRoute } from 'react-router' import CoreLayout from 'layouts/CoreLayout/CoreLayout' import HomeView from 'views/HomeView/HomeView' import ReactSample from 'views/ReactSample' import ReduxSample from 'views/ReduxSample' import ReduxAsyncSample from 'views/ReduxAsyncSample' export default (store) => ( )

Slide 98

Slide 98 text

routing // src/routes/index.js import React from 'react' import { Route, IndexRoute } from 'react-router' import CoreLayout from 'layouts/CoreLayout/CoreLayout' import HomeView from 'views/HomeView/HomeView' import ReactSample from 'views/ReactSample' import ReduxSample from 'views/ReduxSample' import ReduxAsyncSample from 'views/ReduxAsyncSample' export default (store) => ( ) pathͰύεΛࢦఆ IndexRoute path=‘users/:id/edit’ ͳͲ΋Մೳ

Slide 99

Slide 99 text

Component // src/views/HomeView/HomeView.js import React, { Component, PropTypes } from 'react' import { connect } from 'react-redux' import { Link, withRouter } from 'react-router' export class HomeView extends Component { render () { const toReduxAsync = () => this.props.router.push('redux-async') return ( ...

react sample

redux sample

redux-async sample ... ) } } HomeView.propTypes = { router: PropTypes.shape({push: PropTypes.func}) } export default connect(state => state)(withRouter(HomeView))

Slide 100

Slide 100 text

Component // src/views/HomeView/HomeView.js import React, { Component, PropTypes } from 'react' import { connect } from 'react-redux' import { Link, withRouter } from 'react-router' export class HomeView extends Component { render () { const toReduxAsync = () => this.props.router.push('redux-async') return ( ...

react sample

redux sample

redux-async sample ... ) } } HomeView.propTypes = { router: PropTypes.shape({push: PropTypes.func}) } export default connect(state => state)(withRouter(HomeView)) ͷ͔ΘΓʹ

Slide 101

Slide 101 text

Component // src/views/HomeView/HomeView.js import React, { Component, PropTypes } from 'react' import { connect } from 'react-redux' import { Link, withRouter } from 'react-router' export class HomeView extends Component { render () { const toReduxAsync = () => this.props.router.push('redux-async') return ( ...

react sample

redux sample

redux-async sample ... ) } } HomeView.propTypes = { router: PropTypes.shape({push: PropTypes.func}) } export default connect(state => state)(withRouter(HomeView)) router.push withRouterͰίϯϙʔωϯτͷ propsʹrouterΛInject

Slide 102

Slide 102 text

͜͜·Ͱͷ·ͱΊ • React, Redux, Redux-thunk, React-Router • தن໛ఔ౓ͷWebΞϓϦΛߏஙͰ͖Δʢ͸ͣʣ • ςετ • ॻ͖·͠ΐ͏

Slide 103

Slide 103 text

enzyme

Slide 104

Slide 104 text

enzyme • air-bnb੡ReactςετϢʔςΟϦςΟ • Reactίϯϙʔωϯτςετ͕ΊͬͪΌ؆ ୯ʹॻ͚Δʂ

Slide 105

Slide 105 text

αϯϓϧ // tests/views/ReactSample.spec.js import React from 'react' import { shallow } from 'enzyme' import assert from 'power-assert' import ReactSample from 'views/ReactSample' import TodoItem from 'views/ReactSample/TodoItem' describe('ReactSample', function () { it('Should include an

with \'React Sample\' text.', () => { const wrapper = shallow() assert(wrapper.childAt(0).type() === 'h1') assert(wrapper.childAt(0).text() === 'React Sample') }) it('should render three items', () => { const wrapper = shallow() assert(wrapper.find(TodoItem).length === 3) }) })

Slide 106

Slide 106 text

shallow rendering • ωετͨ͠Reactίϯϙʔωϯτ͸ͦͷ··

Slide 107

Slide 107 text

shallow rendering

React Sample

Todos

add todo {'learn react'} {'learn flux'} {'learn redux'}

state:

...
shallow()

Slide 108

Slide 108 text

shallow rendering • ࢠίϯϙʔωϯτͷৄࡉΛແࢹͨ͠ςετ ͕ॻ͚Δ • ࢠίϯϙʔωϯτʹมߋ͕͋ͬͯ΋յΕͳ ͍ςετ • ࢠίϯϙʔωϯτ΁ͷೖྗ·ͰΛςετ

Slide 109

Slide 109 text

shallow rendering it('passes text to TodoItem', () => { const wrapper = shallow() assert.deepEqual(wrapper.state('todos'), ['learn react', 'learn flux', 'learn redux']) assert(wrapper.find(TodoItem).length === 3) assert(wrapper.find(TodoItem).at(0).props().children === 'learn react') assert(wrapper.find(TodoItem).at(1).props().children === 'learn flux') assert(wrapper.find(TodoItem).at(2).props().children === 'learn redux') })

Slide 110

Slide 110 text

Full rendering (mount) • DOM΁ͷ࡞༻Λςετ͢Δ৔߹ • ϥΠϑαΠΫϧϝιουͷςετ • ࢠίϯϙʔωϯτ΋ϨϯμϦϯά

Slide 111

Slide 111 text

Full rendering (mount) it('should render todo texts', () => { const wrapper = mount() assert(wrapper.find('div').at(1).text() === '1: learn react ') assert(wrapper.find('div').at(2).text() === '2: learn flux ') assert(wrapper.find('div').at(3).text() === '3: learn redux ') })

Slide 112

Slide 112 text

Full rendering (mount) import React from 'react' import { mount } from 'enzyme' import { spy } from 'sinon' import assert from 'power-assert' import { ReduxAsyncSample } from 'views/ReduxAsyncSample/ReduxAsyncSample' describe('ReduxAsyncSample', () => { it('Should call fetch when componentDidMount', () => { spy(ReduxAsyncSample.prototype, 'componentDidMount'); const onFetch = spy() const wrapper = mount( ) assert(ReduxAsyncSample.prototype.componentDidMount.calledOnce) assert(onFetch.calledOnce) }) }) ˞XSJUF@NPVOU@UFTUϒϥϯνࢀর

Slide 113

Slide 113 text

·ͱΊ • React: ViewϥΠϒϥϦ • Redux: ΞϓϦέʔγϣϯͷঢ়ଶ؅ཧ • React-Redux: ReactͱReduxͷ࿈ܞ • Redux-Thunk: ඇಉظॲཧ • React-Router: URLϧʔςΟϯά • enzyme: ReactίϯϙʔωϯτςετϢʔςΟϦςΟ

Slide 114

Slide 114 text

reference • http://qiita.com/advent-calendar/2014/reactjs • ҰਓReact.js Advent Calendar 2014 • एׯݹ͍͚Ͳೖ໳ʹ • https://speakerdeck.com/koba04/the-state-of-react-dot-js-2016 • The state of React.js 2016 • React࠷৽ͷಈ޲ɺҰਓAdvent Calendar͔ΒͷΩϟονΞοϓ • https://facebook.github.io/react/docs • http://redux.js.org/ • ެࣜυΩϡϝϯτ͕ॆ࣮ • http://postd.cc/getting-started-with-tdd-in-react/ • ReactͰTDDʢςετۦಈ։ൃʣΛ࢝ΊΑ͏ : ؀ڥߏங͔Βςετ࡞੒ɺػ ೳ࣮૷·ͰͷৄղΨΠυ • ຊ౰ʹθϩ͔ΒTDDͰTodoΞϓϦΈ͍ͨͳͷΛ࡞ΕΔ

Slide 115

Slide 115 text

reference • Web+DB Press vol. 87 • @teppeis͞ΜͷES2015ಛू • http://www.slideshare.net/teppeis/effective-es6 • @teppeis͞Μʹࡢ೥ฐࣾͷࣾ಺ษڧձͰൃද͍͍ͯͨͩͨ࣌͠ͷεϥΠυ • http://qiita.com/kuy • Redux, redux-sagaͷهࣄΛ୔ࢁॻ͍͍ͯΔ@kuy͞Μ • ൃԻ͸ʮΧΠʯ͞Μ

Slide 116

Slide 116 text

reference • http://redux.js.org/docs/basics/UsageWithReact.html • https://medium.com/@dan_abramov/smart-and-dumb- components-7ca2f9a7c7d0 • ίϯϙʔωϯτΛContainer/ViewͰ෼͚Δ • https://facebook.github.io/flux/docs/flux-utils.html#best-practices • facebook͕ڍ͛ͨfluxΞʔΩςΫνϟͰͷϕετϓϥΫςΟε • https://www.gitbook.com/book/tonyhb/redux-without-profanity/details • Dockerࣾ಺ͰReduxΛ࢖ͬͨϊ΢ϋ΢Λॻ͍ͨgitbook • StoreͱαʔόʔͷσʔλΛಉظͤ͞Δ࿩ • https://speakerdeck.com/chrisui/real-world-redux • Immutable.jsΛ࢖ͬͨStore, localStorageͰͷӬଓԽͳͲ • https://speakerdeck.com/joere/flowtype-with-flow • FlowtypeͰ੩తܕνΣοΫ ͭ͘ΕΔˠΩϨΠʹεέʔϧ͢Δͭ͘Γʹ͢Δ

Slide 117

Slide 117 text

͓ΘΓ