React+ReduxでAJAXを使ったアプリを作ってみる
April 16, 2017
Redux 公式のページで紹介されている AJAX を使ったアプリをベースに、 GitHub の公開リポジトリを取得するアプリを作ってみました。
動作イメージ ↓
環境
- react 15.5.4
- redux 3.6.0
- react-redux 5.0.4
- redux-thunk 2.2.0
手順
大まかなステップだけ説明します。
1. State を決める
最初にアプリケーション全体の State を決めます。 今回は以下のように単純な形でいきます。
{
username: "saitoxu",
isFetching: false,
repos: [
{ name: "repo1" },
{ name: "repo2" },
{ name: "repo3" }
]
}
2. Action の定義
Action はリクエストを送るときのREQUEST_REPOS
と、
レスポンスを受け取ったときのRECEIVE_REPOS
の 2 種類定義します。
GitHub の公開リポジトリを取得するのは Fetch API を使いました。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const REQUEST_REPOS = 'REQUEST_REPOS' | |
export const RECEIVE_REPOS = 'RECEIVE_REPOS' | |
export function fetchReposIfNeeded(username) { | |
return (dispatch, getState) => { | |
if (shouldFetchRepos(getState(), username)) { | |
return dispatch(fetchRepos(username)) | |
} | |
} | |
} | |
function fetchRepos(username) { | |
return (dispatch) => { | |
dispatch(requestRepos(username)) | |
return fetch(`https://api.github.com/users/${username}/repos`) | |
.then((response) => { | |
return response.json() | |
}) | |
.then((json) => { | |
if (json.message) { | |
throw Error(json.message) | |
} | |
return dispatch(receiveRepos(username, json)) | |
}) | |
.catch((err) => { | |
return dispatch(receiveRepos(username, [])) | |
}) | |
} | |
} | |
function requestRepos(username) { | |
return { | |
type: REQUEST_REPOS, | |
username | |
} | |
} | |
function receiveRepos(username, json) { | |
return { | |
type: RECEIVE_REPOS, | |
username, | |
repos: json.map(child => { | |
return { name: child.name } | |
}) | |
} | |
} | |
function shouldFetchRepos(state, username = '') { | |
const currentUsername = state.username | |
const isFetching = state.isFetching | |
if (username.length === 0 || currentUsername === username || isFetching) { | |
return false | |
} | |
return true | |
} |
3. Reducer を作る
次に、Reducer を作ります。 今回は State が単純なので、Reducer は 1 つだけで OK です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { combineReducers } from 'redux' | |
import { | |
REQUEST_REPOS, | |
RECEIVE_REPOS | |
} from './actions' | |
function reposByUser(state = { username: '', isFetching: false, repos: [] }, action) { | |
switch (action.type) { | |
case REQUEST_REPOS: | |
return Object.assign({}, state, { | |
username: action.username, | |
isFetching: true | |
}) | |
case RECEIVE_REPOS: | |
return Object.assign({}, state, { | |
isFetching: false, | |
repos: action.repos | |
}) | |
default: | |
return state | |
} | |
} | |
const rootReducer = combineReducers({ | |
reposByUser | |
}) | |
export default rootReducer |
4. Store を作る
Reducer から Store を作るのは簡単です。
非同期リクエストを扱うので、redux-thunk で提供されているthunkMiddleware
をかませます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createStore, applyMiddleware } from 'redux' | |
import thunkMiddleware from 'redux-thunk' | |
import rootReducer from './reducers' | |
export default function configureStore(preloadedState) { | |
return createStore( | |
rootReducer, | |
preloadedState, | |
applyMiddleware( | |
thunkMiddleware | |
) | |
) | |
} |
残りはGistにあげているので確認してみてください。