ここが一番わかりやすかった

happypoulp/redux-tutorial

Basic

import {
  createStore,
  combineReducers
} from "redux"

/**
  * reducer は function
  * @params state :: 前のstate
  * @params state :: 前のstate
  */
let reducer = (state = {}, action) => {
  switch (action.type) {
    case "FOO":
      // 次の state を返す
      return Object.assign({}, state, {
        foo: action.foo
      })
    default:
      return state
  }
}

/**
  * store は function である reducer を引数にとり、生成
  */
let store = createStore(reducer)

// store.getState() で 現在の state を取得できる
console.log(store.getState())
//=> {}

// action は 適当な引数と :type key を含む object を返す関数
let fooAction = (foo) => {
  return {
    type: "FOO",
    foo
  }
  /**
    * NOTE: the above code is the same as:
    * return {
    *   type: "FOO",
    *   foo: foo
    * }
    */
}

// store.dispatch で action を渡してイベントを発火し、状態を更新
store.dispatch(fooAction("hogehoge"))

console.log(store.getState())
//=> { foo: "hogehoge" }

combineReducer

  • 複数のreducerを一つにまとめてくれる
  • このまとめられたやつを createStore に渡す
let userReducer = (state = {}, action) => {
  switch (action.type) {
    case "CHANGE_NAME":
      return Object.assign({}, state, {
        name: action.name
      })
    default:
      return state
  }
}

let itemReducer = (state = [], action) => {
  switch (action.type) {
    case "ADD_ITEM":
      return [
        ...state, 
        action.newItem
      ]
    default:
      return state
  }
}

/**
  * key 名を省略すれば, 以下のように解釈される
  * {
  *   userReducer: userReducer,
  *   itemReducer: itemReducer
  * }
  */
let reducer = combineReducer({
  user: userReducer,
  item: itemReducer
})

console.log(store.getState())
//=> { user: {}, item: [] }

store.subscribe

  • dispatch されたタイミングで callback 的な感じで処理を走らせたい時(いまいち使いどころがわかってない)
store.subscribe(() => {
  console.log("published!")
})

store.dispatch(someAction())
//=> "published!"

Minimal Redux Sample

https://gist.github.com/mmyoji/d282d75ca3dcc60b5710

import React, { PropTypes } from "react"
import { render } from "react-dom"
import { Provider, connect } from "react-redux"
import { createStore } from "redux"

/* Actions */
let assignValue = (value) => {
  return {
    type: "ON_KEYUP",
    value
  }
}

/* Reducer */
const defaultState = {
  value: ""
}
let reducer = (state = defaultState, action) => {
  switch (action.type) {
  case "ON_KEYUP":
    return Object.assign({}, state, {
      value: action.value
    })
  default:
    return state
  }
}

/* Store */
let store = createStore(reducer)

/* Component(Container) */
let App = ({ value, onKeyup }) => {
  let input

  return (
    <div>
      <h2>{value}</h2>
      <input
        ref={node => { input = node }}
        type="input"
        onChange={(e) => onKeyup(input.value)}
        value={value}
      />
    </div>
  )
}
App.propTypes = {
  value: PropTypes.string.isRequired,
  onKeyup: PropTypes.func.isRequired
}

const mapStateToProps = (state) => {
  return {
    value: state.value
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onKeyup: (value) => dispatch(assignValue(value))
  }
}

let MainApp = connect(
  mapStateToProps,
  mapDispatchToProps
)(App)

render(
  <Provider store={store}>
    <MainApp />
  </Provider>,
  document.getElementById("app")
)

Async

  • applyMiddleware, thunkMiddleware あたりを使う
  • 非同期処理は action で 行い、 dispatch を引数に取る関数を返す

http://redux.js.org/docs/advanced/AsyncActions.html

import thunkMiddleware from "redux-thunk"
import { createStore, applyMiddleware } from "redux"
import reducer from "../reducers"

let store = createStore(
  reducer,
  applyMiddleware(
    thunkMiddleware
  )
)

// actions
import fetch from "isomophix-fetch"

function fetchPosts(subreddit) {
  return dispatch => {
    dispatch(requestPosts(subreddit))
    return fetch(`http://www.reddit.com/r/${subreddit}.json`)
      .then(response => response.json())
      .then(json => dispatch(receivePosts(subreddit, json)))
  }
}