Edited at

ステップバイステップで Redux で非同期処理を行う React コンポーネントを作る

More than 3 years have passed since last update.


概要

1日 Redux を触って既存のコンポーネントを書き換えていきました。サンプルやドキュメントはいきなり完成形のコンポーネントについて説明するものが多く分かりづらかったので、忘備録としてここにまとめます。

ステップバイステップで単純なコンポーネントから Redux で非同期処理を行うコンポーネントを作っていきます。

Redux のセットアップや store の作り方などは触れません。


1. Stateless Component を作る

固定の情報を表示するだけのものを作ります


HelloComponent.js

export default function HelloComponent(props) {

return <p>Hello, {props.name}!</p>
}

HelloComponent.propTypes = {
PropTypes.string.isRequired
}



2. Redux と接続する

一枚岩 state から必要な情報を加工して props に変換する mapStateToProps を追加します。

connect() に渡して Redux と接続されたコンポーネントを作ります。


HelloComponent.js

function HelloComponent(props) {

return <p>Hello, {props.name}!</p>
}

HelloComponent.propTypes = {
PropTypes.string.isRequired
}

// ログイン済みユーザーの情報 state.user からユーザー名を取り出して props.name に変換する
function mapStateToProps(state) {
return {
name: state.user && state.user.name
}
}

export default connect(mapStateToProps)(HelloComponent)



3. イベントハンドリング

押した時にログアウトを行うボタンを追加します


Action を作成する

ログアウトする Action Creator を作ります。


  • アクションを識別する type の文字列

  • アクションを生成する関数 (Action Creator)


actions.js

export const SET_USER = "SET_USER"

// Action Creator
export function setUser(user) {
return {
type: SET_USER,
user: user
}
}


Action はこの Action Creator が返すオブジェクトのことです


Reducer を作成する

SET_USER に対応する state の遷移を実装します


reducers.js

import { combineReducers } from "redux"

import { SET_USER } from "./actions"

function user(state = null, action) {
switch(action.type) {
case SET_USER:
return action.user
default:
return state
}
}

export default combineReducers({
user
})



Component 側の変更

ボタンを押した時に state.user を null にしてログアウトしたことにします。

onClick のようなイベントからアクションを生成する mapDispatchToProps を追加します。


HelloComponent.js

import { setUser } from "./actions"

function HelloComponent(props) {
return <div>
<p>Hello, {prop.name}!</p>
<button onClick={props.onClickLogout}>Logout</button>
</div>
}

HelloComponent.propTypes = {
PropTypes.string.isRequired
}

function mapStateToProps(state) {
return {
name: state.user && state.user.name
}
}

function mapDispatchToProps(dispatch) {
return {
onClickLogout: () => { dispatch(setUser(null)) }
}
}

export default connect(mapStateToProps, mapDispatchToProps)(HelloComponent)



4. 非同期処理

一番簡単だと思った redux-thunk ミドルウェアによる方法を説明します。


Action の変更

ログアウト API を叩き、成功時に setUser() で null をセットする非同期 Action を追加します。

非同期 Action は dispatch を引数にとる関数を返す関数として定義されます。

この関数は redux-thunk ミドルウェアが導入されていない場合エラーになります。


actions.js

export const SET_USER = "SET_USER"

// Action Creator
export function setUser(user) {
return {
type: SET_USER,
user: user
}
}

export const logout = () = dispatch => {
exampleApi.logout(error => {
if (!error) {
dispatch(setUser(null))
}
})
}



コンポーネントの変更

setUser(null) を logout() に変えます。


HelloComponent.js

import { logout } from "./actions"

function HelloComponent(props) {
return <div>
<p>Hello, {prop.name}!</p>
<button onClick={props.onClickLogout}>Logout</button>
</div>
}

HelloComponent.propTypes = {
PropTypes.string.isRequired
}

function mapStateToProps(state) {
return {
name: state.user && state.user.name
}
}

function mapDispatchToProps(dispatch) {
return {
onClickLogout: () => { dispatch(logout()) }
}
}

export default connect(mapStateToProps, mapDispatchToProps)(HelloComponent)



所感

Redux を始めたばかりですが、思ったより地道に書き換える事ができてよかったです。非同期処理まわりはいろんな方法があるそうなので、規模に合わせていい感じの書き方を学んでいきたいと思います。