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

React/Redux Introduction

adwd
July 08, 2016

React/Redux Introduction

adwd

July 08, 2016
Tweet

More Decks by adwd

Other Decks in Programming

Transcript

  1. React/Redux Introduction
    2016/7/8

    View Slide

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

    View Slide

  3. View Slide

  4. View Slide

  5. ελϯόΠ؅ཧը໘೥ද
    2015/5 ~

    2015/5
    jQuery
    2015/7
    AngularJS 1
    2015/12
    React/Redux

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  12. 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

    View Slide

  13. 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͕ඞཁ

    View Slide

  14. 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
    ...
    }

    View Slide

  15. 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

    View Slide

  16. JSX
    class NewComponent extends React.Component {
    render() {
    const todos = ['eat', 'sleep']
    return (


    { todos.map(item => {item}) }


    );
    }
    }

    eat
    sleep

    View Slide

  17. 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

    )
    //

    View Slide

  18. αϯϓϧϓϩδΣΫτ

    View Slide

  19. ηοτΞοϓ
    // 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!

    View Slide

  20. αϯϓϧϓϩδΣΫτ
    • 3ͭͷTodoΞϓϦ
    • ReactͷΈ
    • React/Redux
    • React/Redux + REST API

    5PEPͷδϨϯϚ
    w 3FEVYͰෳࡶͳঢ়ଶΛ؅ཧͰ͖Δˠαϯϓϧ
    ʹͦΜͳෳࡶͳͷΛग़ͤͳ͍ˠ݁ہ5PEP

    View Slide

  21. αϯϓϧϓϩδΣΫτ
    • React, Redux, React-router, Babel, ESLint, Webpackͳ
    Ͳ͕͍͍ײ͡ʹઃఆ͞ΕͨϘΠϥʔϓϨʔτ͔Βfork
    • https://github.com/davezuko/react-redux-starter-kit
    • มߋ఺:
    • react/reduxαϯϓϧίʔυͷ௥Ճɺmaterial-uiͷ௥
    ՃͳͲ

    forkݩ͸େن໛ͳॻ͖׵͕͑ਐΈશવҧͬͯ·͢

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  26. ESLintͷεεϝ

    • ྫɿ Atom + linter-eslint

    View Slide

  27. React

    View Slide

  28. React

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

    View Slide

  29. React࠷খߏ੒

    // index.html




    src=“/foo/bar/sample.js"/><br/></body><br/></html><br/>// sample.js<br/>import React from 'react'<br/>import ReactDOM from 'react-dom'<br/>import MyComponent from './MyComponent'<br/>class Example extends React.Component {<br/>render () {<br/>return (<br/><div><br/><h1>Hello, world!</h1><br/><MyComponent<br/>message='Hello, world!' /><br/></div><br/>)<br/>}<br/>}<br/>ReactDOM.render(<br/><Example />,<br/>document.getElementById('example')<br/>)<br/>

    View Slide

  30. React࠷খߏ੒

    // index.html




    src=“/foo/bar/sample.js"/><br/></body><br/></html><br/>// sample.js<br/>import React from 'react'<br/>import ReactDOM from 'react-dom'<br/>import MyComponent from './MyComponent'<br/>class Example extends React.Component {<br/>render () {<br/>return (<br/><div><br/><h1>Hello, world!</h1><br/><MyComponent<br/>message='Hello, world!' /><br/></div><br/>)<br/>}<br/>}<br/>ReactDOM.render(<br/><Example />,<br/>document.getElementById('example')<br/>)<br/>React.Component<br/>

    View Slide

  31. 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}
    )
    }
    }

    View Slide

  32. 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ϝιουΛ࣋ͭΫϥε

    View Slide

  33. 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Λ࣋ͭ

    View Slide

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

    This is My Component

    {this.props.children}


    )
    }
    }

    learn react
    learn flux

    props.children

    View Slide

  35. 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ʹೖΔ

    View Slide

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

    View Slide

  37. αϯϓϧΛ͞ΘͬͯΈΔ

    View Slide

  38. Components
    • TodoList
    • TodoΛstateͱͯ͠؅ཧ
    • TodoItem
    • TodoΛҰͭදࣔ
    • Todo࡟আϘλϯΛදࣔ

    5PEP-JTU
    5PEP*UFN
    ˣ5PEP-JTUίϯϙʔωϯτͷ
    TUBUFΛͦͷ··දࣔ

    View Slide

  39. TodoList state

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

    View Slide

  40. TodoList
    render()

    render () {
    return (

    React Sample
    Todos
    onChange={this.changeText} />
    add todo
    {
    this.state.todos.map((text, index) => (
    onRemove={this.deleteTodoItem} key={index}>
    {text}

    ))
    }


    )
    }

    View Slide

  41. TodoList
    render()

    render () {
    return (

    React Sample
    Todos
    onChange={this.changeText} />
    add todo
    {
    this.state.todos.map((text, index) => (
    onRemove={this.deleteTodoItem} key={index}>
    {text}

    ))
    }


    )
    }

    View Slide

  42. 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 ίϯετϥΫλ

    View Slide

  43. 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 ίϯετϥΫλ

    ঢ়ଶͷॳظԽ

    View Slide

  44. 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)
    })
    }

    View Slide

  45. 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ͷࢦఆͨ͠ύε
    ͚ͩߋ৽͢Δ

    View Slide

  46. 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)} />

    );
    }
    }

    View Slide

  47. 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()

    View Slide

  48. 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: ࣮ߦ࣌ܕνΣοΫ

    View Slide

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

    View Slide

  50. ࣮ࡍʹ͍ͬͯ͡ΈΔ

    View Slide

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

    View Slide

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

    View Slide

  53. Redux

    View Slide

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

    View Slide

  55. ୯ํ޲σʔλϑϩʔ

    IUUQXXXTMJEFTIBSFOFU+POBT0IMTTPOVTJOHSFEVY

    View Slide

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

    View Slide

  57. 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",
    }

    View Slide

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

    View Slide

  59. 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” ]

    View Slide

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

    View Slide

  61. 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’ ]
    })

    View Slide

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

    View Slide

  63. react-redux

    View Slide

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

    View Slide

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

    View Slide

  66. react-redux
    class TodoList extends React.Component {
    render() {

    ‘learn react' })} />
    {
    this.props.todo.map((text, index) => {

    {text}

    })
    }

    }
    }

    store.dispatch
    store.getState().todo

    View Slide

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

    View Slide

  68. react-redux: connect

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

    View Slide

  69. react-redux: connect

    import { connect } from 'react-redux'
    class TodoList extends React.Component {
    render() {

    'learn redux'})} />
    {
    this.props.todo.map((text, index) => {
    {text}
    })
    }

    }
    }
    const connectedTodoList = connect(
    state => { todo: state.todo }
    )(TodoList)
    export default connectedTodoList
    {
    "todo": "...",
    "users": "...",
    "foo": "..."
    }

    View Slide

  70. react-redux: connect

    import { connect } from 'react-redux'
    class TodoList extends React.Component {
    render() {

    'learn redux'})} />
    {
    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

    View Slide

  71. react-redux: Provider

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

    View Slide

  72. 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')
    )

    View Slide

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

    View Slide

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

    View Slide

  75. αϯϓϧΛ͞ΘͬͯΈΔ

    View Slide

  76. 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())

    View Slide

  77. // 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

    View Slide

  78. 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 (

    onChange={(e) => dispatch(editNewTodo(e.target.value)} />
    ...

    )
    }
    }
    const ConnectedReduxSample = connect(
    state => ({todo: state.todo})
    )(ReduxSample)
    export default ConnectedReduxSample

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  82. ඇಉظॲཧ

    View Slide

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

    View Slide

  84. ඇಉظαϯϓϧ

    server: localhost:3000/api/
    • GET todos
    • POST todos/add
    • DELETE todos/:index
    source: server/api/todos.js
    2 seconds
    delay

    View Slide

  85. αϯϓϧΛ͞ΘͬͯΈΔ

    View Slide

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

    View Slide

  87. Async Action

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

    View Slide

  88. 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
    })
    }
    }

    View Slide

  89. // 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ΞΫγϣϯ

    View Slide

  90. 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
    }
    }

    View Slide

  91. 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)

    View Slide

  92. 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ʹϦΫΤετΛૹ৴

    View Slide

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

    View Slide

  94. react-router

    View Slide

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

    View Slide

  96. ओͳAPI

    • ,
    • ϧʔςΟϯάΛߦ͏


    • ͷ໾ׂ
    • show users
    • withRouter
    • ReactίϯϙʔωϯτͷpropsʹrouterػೳΛInject
    • withRouter(MyComponent)

    View Slide

  97. 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) => (






    )

    View Slide

  98. 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’ ͳͲ΋Մೳ

    View Slide

  99. 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))

    View Slide

  100. 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))
    ͷ͔ΘΓʹ

    View Slide

  101. 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

    View Slide

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

    View Slide

  103. enzyme

    View Slide

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

    View Slide

  105. αϯϓϧ

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

    View Slide

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

    View Slide

  107. shallow rendering


    React Sample
    Todos

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

    ...


    shallow()

    View Slide

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

    View Slide

  109. 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')
    })

    View Slide

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

    View Slide

  111. 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 ')
    })

    View Slide

  112. 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)
    })
    })
    ˞[email protected]@UFTUϒϥϯνࢀর

    View Slide

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

    View Slide

  114. 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ΞϓϦΈ͍ͨͳͷΛ࡞ΕΔ

    View Slide

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

    View Slide

  116. 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Ͱ੩తܕνΣοΫ

    ͭ͘ΕΔˠΩϨΠʹεέʔϧ͢Δͭ͘Γʹ͢Δ

    View Slide

  117. ͓ΘΓ

    View Slide