LoginSignup
5
6

More than 5 years have passed since last update.

redux sagaでローディングのアイコンをどう出すか

Last updated at Posted at 2019-01-20

はじめに

apiのfetch中はローディングのアイコンを出す、ということをやりたい時のtipsです。
コードはあくまで簡易的なものなので、雰囲気を捉えていただければ。。

方法

redux sagaを使っている場合、sagaからaxiosやfetchなどを使ってapiのリクエストを送ると思いますが、以下のような感じのリクエストのラッパーを作って、それを用いてapiを叩くようにします。

utils/request.js
import axios from 'axios';

class Request {
  async get(url) {
    const res = await axios.get(url);

    return res;
  }
}

export default Request;

// ↑これをsagaのapi叩くところで使っていく。
// import Request from 'utils/request';
// const request = new Request();
// await request.get('apiのurl')

次は、apiのfetch開始、fetch終了のaction、reducerを定義します。

actions/common.js
export const SHOW_LOADING = Symbol('SHOW_LOADING');
export const HIDE_LOADING = Symbol('HIDE_LOADING');

export const showLoading = () => ({ type: SHOW_LOADING });
export const hideLoading = () => ({ type: HIDE_LOADING });

storeのcommon.isLoadingがtrueの時はローディングアイコンを表示するようにします。

reducer/common.js
import * as commonActions from 'actions/common';

const initialState = {
  isLoading: false,
};

export default (state = initialState, action) => {
  switch (action.type) {
    case commonActions.SHOW_LOADING:
      return { ...state, ...{ isLoading: true } };
    case commonActions.HIDE_LOADING:
      return { ...state, ...{ isLoading: false } };
    default:
      return state;
  }
};

先ほど作成したリクエストのラッパー内を修正します。
reduxのstoreに生えているdispatchを利用する形で、SHOW_LOADINGとHIDE_LOADINGのアクションをdispatchします。

utils/request.js
import axios from 'axios';
import { showLoading, hideLoading } from 'actions/common';
import store from 'store'; // reduxのstore

class Request {
  async get(url) {
    store.dispatch(showLoading());

    const res = await axios.get(url).finally(() => store.dispatch(hideLoading()));

    return res;
  }
}

export default Request;

これで、utils/request.jsを利用してGETのapiリクエストを行った際は、common.isLoadingがfalse => true => falseと遷移するようになりました。

あとは、アプリのルートコンポーネント配下にローディングアイコンのコンポーネントを加え、common.isLoadingの状態で表示/非表示をするようにします。ルートのコンポーネント配下に加えることで、あらゆるページでローディングアイコンが表示されます。

App.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Loading from 'component/commons/Loading';

class App extends Component {
  render() {
    const { common } = this.props;

    return (
      <div>
        <Loading isLoading={common.isLoading} />
        <アプリのルートコンポーネント />
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
    common: state.common,
  };
}

export default connect(mapStateToProps)(App);

ローディングのコンポーネントはあらゆるページの上に被さって表示されるような感じにします。

component/commons/Loading/index.jsx
import React from 'react';
import CircularProgress from '@material-ui/core/CircularProgress';
import style from './style.styl';

const Loading = ({ isLoading }) => (
  <div>
    {isLoading && (
      <div className={style.loadingBox}>
        <div className={style.boxInner}>
          <div className={style.boxBg}>
            <CircularProgress />
          </div>
        </div>
      </div>
    )}
  </div>
);

export default Loading;
component/commons/Loading/style.styl
.loadingBox
  z-index 10
  position fixed
  display table
  width 100%
  height 100%
  background-color rgba(255, 255, 255, .75)

.boxInner
  display flex
  justify-content center
  align-items center
  height 100%
  width 100%

.boxBg
  width 80px
  height 80px

.boxIcon
  width 100%
  height 100%
5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6