LoginSignup
5
1

More than 5 years have passed since last update.

最近Reactのプロジェクトでルーティングにui-routerを使ってみたので使い方などの紹介


この記事はオプトテクノロジーズアドベントカレンダー13日目のエントリーです。

12日目は @ovrmrw さんの「hyper gatling rx」です
14日目は @hiroshist さんの「AWS Serverless関連で何か」です


ざっくり紹介

もともとAngularJSのrouterライブラリとして有名なui-routerがReactでも使えるというもの
なので、お作法だったりAPIだったりがAngularJSを彷彿とさせる感じで、あまりReact文化圏に馴染む感じではないけど、機能としては充実しててとても便利に使える

いいところ

まあなんか普通に便利(雑

  • ui-routerがやはりよく出来ていて、その機能を享受できるのがよい
    • guardがreact-routerと違ってjsの世界だけで書ける
    • ページ遷移時にPromiseがresolveされるまで待つとか便利
    • 他にも調べると色々あって機能的に困るってことはあんまりなさそう
  • Reactとの繋ぎ込みについてはrouterのインスタンス作って <UIRouter>に食わせてあげるだけなので難しいことはない
  • 型定義ファイルが含まれている(そもそもライブラリ自体の実装がts)ので、型定義の情報に安心感があるし、exampleも充実しているのでドキュメント読みに行かなくても型定義ファイルだけで相当便利にやれる

つらめなところ

  • 情報が少ない(日本語情報は皆無だし、英語で調べてもAngularJSでの情報を参考にする事が多かった)
  • Reduxと組み合わせが地味に困る
    • ui-router/reduxはあまりにも機能が足りてない(というかメンテされてない)
    • そのため、自前でインテグレーションを書くことになる
  • ui-routerでのルーティング定義のことを Stateと称しているので、微妙に紛らわしい
  • ReactComponentのpropsへ値をinjectするなど、微妙に型定義に困る動作がある

現在のプロジェクトで使ってみての所感としては、問題なく使えるしui-router固有の問題で困らされたことはあまりなかったので普通に使っていいと思った

使い方とか

サンプルリポジトリ: https://github.com/sisisin-sandbox/ui-router-in-react

普通にReactで使う

最低限これだけ

サンプルコード

import { pushStateLocationPlugin, UIRouter, UIView } from '@uirouter/react';
import React, { Component } from 'react';
import { Home } from './components/Home';
import { Menu } from './components/Menu';
import { User } from './components/User';

const routerStates = [
  {
    name: 'home',
    url: '/home',
    component: Home,
  },
  {
    name: 'user',
    url: '/users/:userId',
    params: {
      userId: {
        type: 'int',
      },
    },
    component: User,
  },
];

class App extends Component {
  render() {
    return (
      <UIRouter states={routerStates} plugins={[pushStateLocationPlugin]}>
        <>
          <Menu />
          <UIView />
        </>
      </UIRouter>
    );
  }
}

export default App;

パラメータを使う

以下のようにrouterのstateの url プロパティで :userIdのように指定すると、parameterを取得するAPIを使って値を取れるようになる。
このときのparamsに関する設定を別途 paramsプロパティで指定可能で、例えば typeを指定すると取得時にその型に変換しておいてくれるなど、便利機能が満載

サンプルコード

const routerStates = [
  {
    name: 'user',
    url: '/users/:userId',
    params: {
      userId: {
        type: 'int',
      },
    },
    component: User,
  },
];

ページ遷移前にPromiseを解決して、その結果をpropsとして扱う

router stateのresolveプロパティに解決したいPromise及び、その結果をpropsにどんなプロパティ名でinjectするかを指定してやる

サンプルコード

  {
    name: 'home',
    url: '/home',
    component: Home,
    resolve: [
      {
        token: 'resolvedValue',  // props.resolvedValueでアクセスが可能になる
        resolveFn: () => Promise.resolve('resolve!'),
      },
    ],
  },

Reactの中でrouterのAPIを使う

サンプルコード

statesで指定したComponentのpropsにrouter周りのインスタンスがinjectされているので、それを使う
この例では パラメータを使うのstateに対応するコンポーネントとして記載。

export const User = ({ transition }: { transition: Transition }) => {
  const { userId } = transition.params();
  return (
    <div>
      user {userId}. typeof userId is {typeof userId}
    </div>
  );
};

実際の表示↓

ss_ 2018-12-14 3.16.52.png

storybookと共存する

サンプルコード

他のrouterライブラリ同様、メモリルーターを使う

import { storiesOf } from '@storybook/react';
import { memoryLocationPlugin, ReactStateDeclaration, UIRouter } from '@uirouter/react';
import React from 'react';
import { Menu } from '../components/Menu';

const plugins = [memoryLocationPlugin];
const states: ReactStateDeclaration[] = [];
storiesOf('Menus', module).add('Menu', () => {
  return (
    <UIRouter states={states} plugins={plugins}>
      <Menu />
    </UIRouter>
  );
});

Reduxと合わせて使う

Reduxと組み合わせて使うときも大体そのまんまでよいが、一部ハマるので注意

  • routerのシングルトンを自前でインスタンス化するときは UIRouter コンポーネントに pluginsstatesを食わせず、routerインスタンスに自分で設定して上げる必要がある

サンプルコード

App.tsx

export const router = new UIRouterReact();  // routerのインスタンスを使いたいので、自前でnewする
const routerStates = [
  {
    name: 'home',
    url: '/home',
    component: Home,
  },
  {
    name: 'user',
    url: '/users/:userId',
    params: {
      userId: {
        type: 'int',
      },
    },
    component: UserConatiner,
  },
];

// 自前でrouterをnewして使う場合はpluginやstatesは事前に解決した上で<UIRouter>コンポーネントにrouterだけを渡すようにしないと実行時エラーになる
router.plugin(servicesPlugin);
router.plugin(pushStateLocationPlugin);
routerStates.forEach(s => router.stateRegistry.register(s));

export const store = createStore(
  combineReducers({
    users: userReducer,
  }),
);

export type AppState = typeof store extends Store<infer S, any> ? S : never;

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <UIRouter router={router}>
          <>
            <Menu />
            <UIView />
          </>
        </UIRouter>
      </Provider>
    );
  }
}

export default App;

その他の実装パターン

ui-router公式で提供しているサンプルリポジトリ があるので、これを見ると大体ある(というか自分が頑張って書いてもこのリポジトリ以上のことはそんなに書けない。。)

まとめ

というわけでui-router使い方紹介でした。
色々出来て便利なのでみんな使おう。

参考資料リンク

5
1
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
1