How to build Rails5 API + Redux ToDo Application その4
前回はクライアントをRedux化(API叩かない版)しました。
これにAPIを叩かせて完成とします。
CORS
実装前に、クロスドメインの問題があるので、Railsのほうでごにょごにょします。
# config/application.rb module Rails5ReactReduxTodo class Application < Rails::Application # ... config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '*', :headers => :any, :methods => [:get, :post, :patch, :delete] end end end end
SuperAgent
AJAXしたいだけなのでjQueryではなくHTTPリクエストに特化したライブラリを利用します。
$ npm install --save superagent
まず、Railsサーバーを起動してREPLからAPIを叩いてみます。
サーバーを起動
$ rails s
REPLから実行
$ node
get: ToDO一覧を取得
> request = require('superagent') > request.get('http://localhost:3000/todos').end(function(err, res) { console.log(res.body) }) > [ { id: 1, title: '単2電池を買いに行く', completed: false, order: 1, created_at: '2016-07-29T00:20:23.797Z', updated_at: '2016-07-29T00:20:23.797Z' } ]
post: ToDOを登録
> request.post('http://localhost:3000/todos').send({ todo: { title: 'ストレッチ', order: 1 }}).end(function(err, res) { console.log(res.body) }) > { id: 2, title: 'ストレッチ', completed: null, order: 1, created_at: '2016-07-29T00:23:17.983Z', updated_at: '2016-07-29T00:23:17.983Z' }
patch: ToDOを完了状態に更新
> request.patch('http://localhost:3000/todos/2').send({ todo: { completed: true }}).end(function(err, res) { console.log(res.body) }) > { id: 2, completed: true, title: 'ストレッチ', order: 1, created_at: '2016-07-29T00:23:17.983Z', updated_at: '2016-07-29T00:25:39.456Z' }
delete: ToDOを削除
> request.del('http://localhost:3000/todos/2').end(function(err, res) { console.log(res.body) }) > {}
こんな感じでWeb APIにアクセスできます。
redux-promise
redux-promiseはcreateActionにPromiseを渡すとよしなにしてくれるMiddlewareです。
redux-actionsと併せて使います。
まずはPromiseを返すWeb APIを作成します。
以下はToDO一覧を取得するAPIの場合です。
// api/todos.js export default { fetchTodos: () => { return new Promise((resolve, reject) => { request .get(url) .end((err, res) => { if (!res || res.status === 404) { reject() } else { resolve(res.body) } }) }) }, /* ... */ }
Action作成時にこの関数を渡します。
// actions/api.js export const fetchTodos = createAction(ApiAction.FETCH_TODOS, api.fetchTodos)
Actionのハンドリングをします。
nextのaction.payloadにはresolveの引数であるbody(todos)が格納されています。
// reducers/todos.js const todos = handleActions({ [ApiAction.FETCH_TODOS]: { next: (state, action) => action.payload, throw: (state, aciton) => [] }, /* ... */ }, [])
このActionをcomponentDidMount時に呼び出します。
// App.js import React, { Component } from 'react' import { connect } from 'react-redux' import * as actionCreators from './actions/api' export default class App extends Component { componentDidMount() { this.handleFetchTodos() } handleFetchTodos() { this.props.fetchTodos() } /* ... */ } App = connect(null, actionCreators)(App) export default App
これを実行しredux-loggerを利用してActionのログを見てみます。
自前でログを仕込んでみたところrequest, responseの間に1回目のFETCH_TODOSが発行されています。
(おそらくこの部分はredux-promiseが処理してくれている)
で、resolveのタイミングで2回目のFETCH_TODOSが発行されhandleActionsのnextがコールされます。
これで非同期でAPIを叩いて動くToDOアプリケーションの完成です。
つぎは何作ろっかなー