React + Reduxはその柔軟性から、Googleで調べて出てくる情報の一貫性がなく、機能を実装するときの調査が結構大変。
今回はリアルタイムチャットツールを開発したときの経験を元にReact + Reduxでサーバー通信を行うときの書き方について紹介していきます。
ちなみに今回の例ではRuby on RailsでAPIを構築し、そちらへ通信を行うという構成です。
使用するライブラリなど
サーバー通信用のライブラリ
javascriptでサーバー通信をするときに用いられるものとしてはajaxがメジャーですが、Reactで開発している中でajaxのためだけにjQueryを用いるというのはちょっとナンセンスです。
そこで今回はaxiosというサーバー通信用のライブラリを使用します。
Reactでサーバー通信する際は他にもsugaragentやfetchが用いられることがありますが、書き方のわかりやすさ、名前のかっこよさから今回axiosをチョイスしました。
ただどのライブラリを使用するにしてもサーバー通信処理を記述する箇所に変わりはありません。
非同期処理用のミドルウェア
React + Redux構成において、stateを書きかえる処理はreducerに記載するのが基本です。しかし、そのstateを書き換えるという処理の都合上、reducerで行われる処理は同期処理でなければならず、ここに非同期のサーバー通信処理を行うことはできません。
そこでactionとreducerの間でサーバー通信を行えるようにするためのミドルウェアであるredux-thunkを導入します。
このミドルウェアを導入することによって、action creater上で関数の呼び出しが可能となります。
サーバー通信をどのタイミングで実装するかという決まりが存在していないため、今回は最もメジャーな手法であるこのaction-reducer間における非同期通信を実装していきます。
サーバー通信を行う為の最低限の構成
ミドルウェアの適用
storeを生成する際に読み込ませることでミドルウェアを実装することができます。
ここでのポイントはファイル構成にかかわらず、createStoreにapplyMiddlewareを使用してredux-trunkをstoreに読み込ませるというところです。
import { createStore, applyMiddleware } from 'redux'; import fromReducer from '../reducers/sample'; import thunkMiddleware from 'redux-thunk' /* Storeの実装 */ export default function configureStore(){ return createStore( fromReducer,applyMiddleware(thunkMiddleware) ) }
アクション
非同期のサーバー通信を実現するために必要なアクションは最低限以下の2つです。
- 通信開始を伝えるアクション
- 通信成功時にデータ更新を行うアクション
あとは失敗時に呼び出すアクションがあると便利ではありますが、今回は割愛します。
GETでデータ取得
まずはGETでサーバーからデータを取得するサーバー通信
前述したように、通信開始時、完了時にそれぞれアクションを呼び出しています。
完了時に呼び出されているアクションにおいて、取得したデータをstoreに渡すといったイメージです。
export const FETCH_MESSAGES = 'FETCH_MESSAGES' export const FETCH_MESSAGES_SUCCESS = 'FETCH_MESSAGES_SUCCESS' function requestMessages() { return { type: FETCH_MESSAGES } } function receiveMessages(json) { return { type: FETCH_MESSAGES_SUCCESS, messages: json } } // メッセージ取得 export function fetchMessages() { return dispatch => { dispatch(requestMessages()) return axios.get('/messages.json').then((response) => { dispatch(receiveMessages(response.data)) }).catch((response) => { console.log(response) }) } }
POSTでデータ送信
POSTでサーバー上のデータを更新する場合も記述箇所、基本的な記述内容は変わりません。
データ送信後、サーバーデータを取得することで現在の画面も最新に更新されます。
export const ADD_MESSAGE = 'ADD_MESSAGE' export const ADD_MESSAGE_SUCCESS = 'ADD_MESSAGE_SUCCESS' function addNewMessage() { return { type: ADD_MESSAGE, }; } function addNewMessageSuccess() { return { type: ADD_MESSAGE_SUCCESS, }; } // メッセージ送信 export function postMessage(messageBody) { return dispatch => { dispatch(addNewMessage()) return axios.post('/messages.json', { body: messageBody },{withCredentials:true} ).then((response) => { dispatch(fetchMessages()) dispatch(addNewMessageSuccess()) }).catch((response) => { console.log(response) }) } }
Reducer
reducerについてはシンプルにサーバー通信用のフラグと取得した値を設定しておきます。
const initialState = { messages: [], isFetching: false, }; export default function sample(state = initialState, action) { switch (action.type) { case 'FETCH_MESSAGES': return Object.assign({}, state, { isFetching: true }); case 'FETCH_MESSAGES_SUCCESS': return Object.assign({}, state, { isFetching: false, messages: action.messages }); case 'ADD_MESSAGE': return Object.assign({}, state, { isFetching: true }); case 'ADD_MESSAGE_SUCCESS': return Object.assign({}, state, { isFetching: false, }); default: return state; } }
サーバー通信用メソッドの呼び出し
あとは実際にaxiosでサーバー通信を行っているメソッドを呼び出せばOKです。
GETの場合
データ取得の場合、初期表示についてはレンダリング後にfetchMessagesを呼び出せばstateが更新されます。
またデータ送信成功時にも同じ関数を呼び出しているため、データを追加したは自動的に追加された状態のデータが表示されます。
componentDidMount() { this.props.fetchMessages() }
POSTの場合
フォームで入力した値をサーバーに送信する機能は以下の通りに作成したデータ送信用関数を呼び出せばOKです。
class FormInput extends React.Component { send(e) { e.preventDefault(); this.props.postMessage(this.myInput.value); this.myInput.value = ''; return; } render() { return ( <form> <input type="text" ref={(ref) => (this.myInput = ref)} defaultValue="" /> <button onClick={(event) => this.send(event)}>Send</button> </form> ); } }
まとめ
以上がRedux + Reactでの非同期サーバー通信の実装方法です。
「動かねえぞ!」って言う場合はお問い合わせなどいただければ、できる限り対応いたします。
閲覧いただき、ありがとうございました。