18
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React.js の Context API で Private Route を作る

Posted at

Reactでログイン機能があるSPAを開発する際に、特定のページ(ルート)を秘匿したい場合があると思います。
今回はReact v16から実装されたContext APIを使ってプライベートなルートを作成します。

Context APIとは

Context APIはProviderとConsumerという機構を使い、異なるコンポーネント間でデータの共有を可能にするものです。大まかな概要は以下の通りです。

  • createContextはProviderとConsumerというコンポーネントのペアを返す
  • Providerはデータとコールバックを保有するコンポーネント
  • ConsumerはProviderのデータを購読しコンポーネントにデータを渡す

環境

ライブラリのバージョン

Library version
react 16.2.0
react-router-dom 4.3.1

ディレクトリ構成

Reactではディレクトリ構成やコンポーネントの分割に規制がなく、アプリケーションの要件に応じて設計することができます。
以下は今回、解説に使うアプリケーションのディレクトリ構成です。あくまで例ですので、実際の開発では適宜設計してください。

index.js
  /contexts
    auth.js
  /routes
    home.js
    mypage.js

1. Contextを作成する

/contextsディレクトリにauth.jsファイルを作成しAuthContextを作成します。

// contexts/auth.js
import React, { Component } from 'react';
// Contextの生成
const AuthContext = React.createContext();
// ContextにはProviderとConsumerというコンポーネントが含まれる
const AuthProvider = AuthContext.Provider;
const AuthConsumer = AuthContext.Consumer;

export { AuthProvider, AuthConsumer };

AuthContextにログイン状態を表すisAuthというデータを管理させるため、Providerをカスタマイズします。

// contexts/auth.js
...
class AuthProvider extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isAuth: false
    }
  }
  // Providerのvalueを設定する。Consumerはこのvalueを購読する。
  render() {
    return (
      <AuthContext.Provider
        value={{
          isAuth: this.state.isAuth
        }}
      >
        {this.props.children}
      </AuthContext.Provider>
    );
  }
}
...

2. Contextからデータを取り出す

AuthContextからisAuthを取り出しコンポーネントに反映させてみます。
Headerというコンポーネントを作成し、ログイン状態に応じてテキストを表示させます。

ProviderでHeaderコンポーネントをラップし、コンポーネント側ではConsumerを使ってデータを取り出します。

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { AuthProvider, AuthConsumer } from './contexts/auth';
// ProviderでHeaderコンポーネントをラップ
const App = () => (
  <AuthProvider>
    <Header />
  </AuthProvider>
);
// Headerコンポーネント中でConsumerからデータを取り出す
const Header = () => (
  <AuthConsumer>
    {({ isAuth }) => (
      <>
        <h1>Header</h1>
        {isAuth ? (
          <p>ログインしているよ!</p>
        ) : (
          <p>ログインしていないよ!</p>
        )}
      </>
    )}
  </AuthConsumer>
);

ReactDOM.render(<App />, document.getElementById('root'));

3. Providerに状態を変更するコールバックを作成

ログイン、ログアウトボタンを作成するために、AuthProviderにログイン状態を切り替えるためのコールバック関数を作成します。
今回はfetch等のAPI通信は割愛します。

// contexts/auth.js
...
class AuthProvider extends Component {
  constructor(props) {
    ...
    // thisをバインド
    this.login = this.login.bind(this);
    this.logout = this.logout.bind(this);
  }
  login() {
    // 実際にはここでAPI等を使ってログイン処理を行う
    setTimeout(() => this.setState({ isAuth: true }), 1000);
  }
  logout() {
    this.setState({ isAuth: false });
  }
  render() {
    return (
      <AuthContext.Provider
        value={{
          isAuth: this.state.isAuth,
          login: this.login,
          logout: this.logout
        }}
      >
        {this.props.children}
      </AuthContext.Provider>
    );
  }
}
...

続いてHeaderコンポーネントにログイン/ログアウトボタンを実装します。

// index.js
...
const Header = () => (
  <AuthConsumer>
    {({ isAuth, login, logout }) => (
      <>
        <h1>Header</h1>
        {isAuth ? (
          <p>ログインしているよ!</p>
          <button onClick={logout}>ログアウト</button>
        ) : (
          <p>ログインしていないよ!</p>
          <button onClick={login}>ログイン</button>
        )}
      </>
    )}
  </AuthConsumer>
);
...

4. プライベートなルートを作成

次はreact-routerを使ってプライベートなルートを作成していきます。
Headerコンポーネントに続いて、react-routerを使ってルーティングを設定します。

// index.js
...
import { BrowserRouter, Route, Redirect, Switch } from 'react-router-dom';
import Home from './routes/home';
import MyPage from './routes/mypage';
// Headerコンポーネントを取り除き、ルーティングを設定
const App = () => (
  <BrowserRouter>
    <AuthProvider>
      <Header />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/mypage" component={MyPage} />
      </Switch>
    </AuthProvider>
  </BrowserRouter>
);
...

続いて、プライベートなルートを作成するための、PrivateRouteコンポーネントを作成します。

// index.js
...
// プライベートなルートを設定するためのコンポーネント
const PrivateRoute = ({ component: Component, ...rest }) => (
  <AuthConsumer>
    {({ isAuth }) => (
      <Route
        render={props =>
          isAuth ? (
            <Component {...props} />
          ) : (
            <Redirect to="/" />
          )
        }
        {...rest}
      />
    )}
  </AuthConsumer>
);
// 秘匿したいルートをPrivateRouteで置き換える
const App = () => (
  <BrowserRouter>
    <AuthProvider>
      <Header />
      <Switch>
        <Route exact path="/" component={Home} />
        <PrivateRoute path="/mypage" component={MyPage} />
      </Switch>
    </AuthProvider>
  </BrowserRouter>
);
...

未ログイン状態でmypageにアクセスすると、トップページにリダイレクトされることが確認できると思います。

大規模なアプリケーションなど厳格な状態管理が必要とされるケースでは、Redux等の状態管理フレームワークが引き続き有用に思いますが、中小規模のアプリケーションにおいてはContextを用いるのも良い選択肢ではないでしょうか。

参照サイト
18
13
2

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
18
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?