TypeScript と react-router と mobx でログインユーザーの状態管理

Shunsuke Sawada

アプリケーションにはログインがつきもの。
React でユーザーがログインしているかどうかを mobx で管理したいってのは自然な流れ。
TypeScript とだとちょっと面倒だったのでメモ。

mobx

https://mobx.js.org/
https://github.com/mobxjs/mobx-react#strongly-typing-inject

Storeの分け方はこのページが良かった。
https://mobx.js.org/best/store.html

mobx-react

https://github.com/mobxjs/mobx-react

mobx-react-router

https://github.com/alisd23/mobx-react-router

TypeScript なので @types/history が必要らしい。

TypeScript

https://www.typescriptlang.org/docs/home.html


ルートコンポーネント

App.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import * as firebase from 'firebase';
import * as React from 'react';

// Route
import createBrowserHistory from 'history/createBrowserHistory';
import { Provider } from 'mobx-react';
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router';
import { Route, Router } from 'react-router-dom';

// Routing store
const browserHistory = createBrowserHistory();
const routingStore = new RouterStore();
const history = syncHistoryWithStore(browserHistory, routingStore);

// Other Store
import AuthStore from './stores/AuthStore';

// Component
import MyComponent from './MyComponent';

// Mobx Root Store
const authStore = new AuthStore();
const stores = {
  auth: authStore,
  routing: routingStore,
};


class App extends React.Component {
  // constructor の方が良いかも
  public componentWillMount() {
    firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        stores.auth.user = user;
      }
    })

    // ユーザーサインアップ(メアドログインとか何でも良い)
    firebase.auth().signInAnonymously()
      .catch((error) => {
        alert(error);
      });
  }

  public render() {
    return (
      <Provider {...stores}>
        <Router history={history}>
          <div>
            <Route exact={true} path="/path/:id" component={MyComponent} />
          </div>
        </Router>
      </Provider>
    );
  }

}

AuthStore

ObservableFirebase.User の型指定の仕方が分からない。
ひとまず any で。
getter のインターフェイスは () => boolean ではないことに注意。

stores/AuthStore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { computed, observable } from 'mobx';

export interface IAuthStore {
  user: any;
  isSignedIn: boolean;
}

export default class AuthStore implements IAuthStore {
  @observable public user = {};

  @computed get isSignedIn(): boolean {
    return this.user !== {};
  }
}


MyComponent

@inject でちょっとハマった。
stores.auth では、

1
Property 'auth' does not exist on type '{}'.

と言われてしまったので [ ] でアクセスしているが、今度は、

1
object access via string literals is disallowed

と怒られたので、仕方がないから黙らせた。

この辺のドキュメントはこちら。
https://github.com/mobxjs/mobx-react#with-typescript

MyComponent.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { inject, observer } from 'mobx-react';
import * as React from 'react';
import { IAuthStore } from './stores/AuthStore';

interface IMyComponentProps {
  match: {
    params: { id: string },
  };
  auth: IAuthStore;
};

@inject(stores => ({
  /* tslint:disable-next-line */ // stores.auth will be error
  auth: stores['auth'] as IAuthStore,
}))
@observer
class MyComponent extends React.Component<IMyComponentProps, any> {
  public render() {
    const { auth } = this.props;
    return (
      <div>
        <h4>{auth.isSignedIn ? 'ログイン中' : 'ログインしてください'}</h4>
      </div>
    );
  }
}
Shunsuke Sawada

おすすめの記事

爆速でウェブアプリを公開する方法(無料でSSL独自ドメイン)/ React + Typescript + Cloud Function + Firebase Hosting
2
CloudFunction と React で共通のコードを読み込んで TypeScript でうまくコンパイルする方法
Firestore の Security Rule を理解する