投稿日
ReactでAPI通信をしている間Loading画面を表示する
こんにちは。西日本テクノロジー&イノベーション室の藤田です。 日々(技術的に)強くなりたいと言っている新卒3年目です。
この記事では、先日まで関わっていたサービス開発案件のノウハウ紹介として、ReactでAPI通信している間にLoading画面を表示する実装について説明します。
背景
React + Railsでログインを必要とするサービス開発をしています。
このサービスは、サービスを利用しているユーザーのログイン状態に応じて振る舞いを切り替えています。
例えば、ユーザーがログイン済みであればユーザー自身の利用履歴が閲覧できますが、ログインしていない状態でそれはできません。ログイン状態に応じて振る舞いが変わるのは、ログインが必須のサービスであれば一般的な仕様だと思います。
ユーザーのログイン状態は、ユーザー自身の情報を取得するAPIを叩いて判断します。ログイン済みであればユーザー情報が返ってきますし、ログインしていなければ401エラーが返ってくるようにしています。
ユーザー情報を取得した結果によって表示するコンポーネントを切り替えるため、ユーザー情報を取得している間に表示するLoading画面が必要でした。 ここでは、Loading画面をどのように切り替えて表示しているのかをコードを例示しながら説明します。
実装
このサービスではLoading画面の切り替えを、最上位のコンポーネントでありルーティングを管理しているApp.js
に実装しました。全てのコンポーネントにユーザー情報を共有する必要があったためです。 以下で、App.js
に実装した内容を説明していきます。
Loading画面を表示するフラグをstateに設定しておく
あらかじめloading
というstate
をtrue
の状態で持っておきます。
constructor(props) {
super(props);
this.state = {
loading: true
};
}
ユーザー情報を取得した後にstateに設定したフラグを切り替える
componentDidMount()
でユーザー情報を取得して、結果をuser
に格納してsetState
します。もしログインしていなかった場合は401エラーが発生するので、空のオブジェクトをuser
に格納し同じようにsetState
します。
また、一連の処理が終わった時点で、loading
をfalse
に設定します。
async componentDidMount() {
try {
const resp= await axios.get("/api/my/profile");
const user = resp.data;
this.setState({ user });
} catch (e) {
if (e.response.status === 401) {
const user = {};
this.setState({ user });
} else {
//401エラー以外のエラーが返ってきた場合の処理を記述
}
}
this.setState({ loading: false });
}
loading
が true
であればLoading画面を表示し、false
であれば、アプリケーションの画面を表示させます。 Loading画面ではAnt Design MobileのActivityIndicatorを使っています。 ユーザー情報はContextを用いて下位コンポーネントに共有しています。
AppContext
はReact.createContext()
で作成したContextオブジェクトです。 ここでは、value
にuser
を指定して下位コンポーネントにユーザー情報を共有しています。
render() {
const { loading, user } = this.state;
const style = { position: "fixed", top: 0, left: 0, width: "100%", height: "100%", display: "flex", justifyContent: "center", alignItems: "center" };
if (loading) {
return (
<div style={style}>
<ActivityIndicator size="large" />
</div>
);
}
return (
<AppContext.Provider value={{ user }}>
<BrowserRouter>
{/*ここで各コンポーネントのルーティングを設定する*/}
</BrowserRouter>
</AppContext.Provider>
);
}
以上の説明を踏まえて、App.js
の全体のコードは以下の通りです。
import React from "react";
import { BrowserRouter, Route } from "react-router-dom";
import axios from "axios";
import { ActivityIndicator } from "antd-mobile";
import AppContext from "./context";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true
};
}
async componentDidMount() {
try {
const resp= await axios.get("/api/my/profile");
const user = resp.data;
this.setState({ user });
} catch (e) {
if (e.response.status === 401) {
const user = {};
this.setState({ user });
} else {
//401エラー以外のエラーが返ってきた場合の処理を記述
}
}
this.setState({ loading: false });
}
render() {
const { loading, user } = this.state;
const style = { position: "fixed", top: 0, left: 0, width: "100%", height: "100%", display: "flex", justifyContent: "center", alignItems: "center" };
if (loading) {
return (
<div style={style}>
<ActivityIndicator size="large" />
</div>
);
}
return (
<AppContext.Provider value={{ user }}>
<BrowserRouter>
{/*ここで各コンポーネントのルーティングを設定する*/}
</BrowserRouter>
</AppContext.Provider>
);
}
}
export default App;
まとめ
API通信を待っている間にLoading画面を表示する方法について説明しました。 背景でも言った通り、汎用的な仕様だと思うので参考にしていただければ幸いです。
本コンテンツはクリエイティブコモンズ(Creative Commons) 4.0 の「表示—継承」に準拠しています。