hina2go

主に技術系のこことか。最近React始めました。

React Routerのチュートリアル Lesson10

前回の続き。

hinathy.hatenablog.com

Lesson10 - Clean URLs with Browser History

これまで作ったアプリのURLは、ハッシュを使ったHackで成り立っています。これは常にうまく動作するデフォルトの挙動です。 しかし、もっと良い方法があります。

モダンなブラウザは、HTTPリクエストを生成することなくURLを操作することが可能です。

browserHistoryを使う

app/index.jsxを開いて、以下のように変更しましょう。 hashHistorybrowserHistoryに変更しただけです。

ReactDOM.render((
  <Router history={browserHistory}>

これだけでクリーンなURLでアクセスすることができます。 しかしこの方法には罠があります。 何らかのリンクをクリックしたのち、ブラウザをリフレッシュしてみましょう。 すると404 Not Foundが発生するはずです。

404 Not Foundの解決方法

webpack-dev-serverの起動オプションに、--history-api-fallbackをつける。

package.jsonを開いて、以下のように変更します。

  "scripts": {
    "start": "webpack-dev-server --inline --content-base . --history-api-fallback",
  },

scriptタグのsrcに指定しているbundle.jsのパスを変更する。

html-webpack-pluginを使っているので、webpack.config.jsを変更します。 以下のように、filenameを変更します。

  output: {
    path: PATHS.build,
    filename: '/bundle.js'
  },

まとめ

  • browserHistoryを使えば、クリーンなURLを使うことができる。

ソースコード

github.com

React Routerのチュートリアル Lesson9

前回の続き。

hinathy.hatenablog.com

Lesson9 - Index Links

今回は、ナビゲーションリンクにHomeリンクを追加して、どの画面からでもホーム画面に戻れるようにします。

最初に思いつく方法

単純にapp/components/app.jsxに、ホーム画面へのリンクを追加する方法です。

<li><NavLink to="/">Home</NavLink></li>

しかし、この方法には問題があります。 AboutをクリックしてもReposをクリックしても、常にHomeがアクティブな状態になることです。 なぜなら、/は全てのURLの親だからです。

IndexLinkを使う

この問題を解決するために、React Routerが提供するIndexLinkを使います。 IndexLinkは、IndexRouteで設定されているURLへ遷移するためのリンクを生成してくれます。 先ほどの、ホーム画面へのNavLinkを削除して、以下を追加しましょう。

<li><IndexLink to="/" activeClassName="active">Home</IndexLink></li>

すると、期待通りに動作するかと思います。

LinkコンポーネントonlyActiveOnIndexプロパティを使う

もう一つの方法は、LinkコンポーネントonlyActiveOnIndexを使うことです。 すでに作成済みのNavLinkは、Linkコンポーネントのラッパーですが、スプレッド演算子でプロパティを展開しているので、以下のように書くことで実現することができます。

<li><NavLink to="/" onlyActiveOnIndex={true}>Home</NavLink></li>

まとめ

ソースコード

github.com

React Routerのチュートリアル Lesson8

前回の続き。

hinathy.hatenablog.com

Lesson8 - Index Routes

これまでの実装では、/にアクセスすると、単にリンク一覧とBlankページが表示されるだけでした。 今回は、Homeコンポーネントを追加して、/にアクセスしたときにHome画面を表示します。

方法1

子要素がないときには、Homeコンポーネントレンダリングする方法です。 具体的には以下のように、{ this.props.children || <Home /> } とします。

class App extends React.Component {

  render() {
    return (
      <div>
        <h1>React Router Tutorial</h1>
        <ul role="nav">
          <li><NavLink to="/about">About</NavLink></li>
          <li><NavLink to="/repos">Repos</NavLink></li>
        </ul>
        { this.props.children || <Home /> }
      </div>
    );
  }
  
}

Homeコンポーネントは以下です。

import React from 'react';

class Home extends React.Component {

  render() {
    return (<div>Home</div>);
  }
  
}

export default Home;

方法2

もう一つの方法は、React Routerが提供するIndexRouteを使う方法です。 方法1のコードを削除して、app/index.jsxを以下のように変更してみます。

ReactDOM.render((
  <Router history={hashHistory}>
    <Route path="/" component={App}>
      <IndexRoute component={Home} />
      <Route path="/repos" component={Repos}>
        <Route path="/repos/:userName/:repoName" component={Repo} />
      </Route>
      <Route path="/about" component={About} />
    </Route>
  </Router>
), document.body.appendChild(document.createElement('div')));

IndexRouteにはパスが設定されません。 IndexRouteは、親のパスに正確に一致する場合にレンダリングするコンポーネントを表示します。

ソースコード

github.com

React Routerのチュートリアル Lesson7

前回の続き。

hinathy.hatenablog.com

Lesson7 - More Nesting

今回は、前回追加したパラメータ付きリンクのクリック時の挙動を、ちょっとだけ変更します。 リンクをクリックすると、その下にリンクしたリポジトリ名を表示するようにします。

ネストを変更する

Reposの子要素にRepoを配置します。

... (snip) ...

        <Route path="/repos" component={Repos}>
          <Route path="/repos/:userName/:repoName" component={Repo} />
        </Route>

... (snip) ...

子要素をリンクの下に描画する

this.props.childrenで、Reposの子要素を描画します。 ついでに、LinkNavLinkに置き換えて、リンクのアクティブな状態がわかるように変更します。

class Repos extends React.Component {

  render() {
    return (
      <div>
        <h2>Repos</h2>
        <ul>
          <li><NavLink to="/repos/reactjs/react-router">React Router</NavLink></li>
          <li><NavLink to="/repos/facebook/react">Facebook</NavLink></li>
        </ul>
        { this.props.children }
      </div>
    );
  }

}

export default Repos;

まとめ

特になし。

ソースコード

github.com

React Routerのチュートリアル Lesson6

前回の続き。

hinathy.hatenablog.com

Lesson6 - Params

今回のお題は、パラメータの渡し方です。

例えば、以下のようなURLを考えてみます。

/repos/reactjs/react-router
/repos/facebook/react

これらのパラメータは、以下の形式にマッチします。

/repos/:userName/:repoName

これらのパラメータは、this.props.params[name]のように取得することができます。

渡されたパラメータを画面に表示してみる

まずは、Repoコンポーネントを作成します。 このコンポーネントは、渡されたパラメータrepoNameを単に表示するのみです。

import React from 'react';

class Repo extends React.Component {

  render() {
    return (
      <div>
        <h2>{this.props.params.repoName}</h2>
      </div>
    );
  }
  
}

export default Repo;

次に、Routeをapp/index.jsxに追加します。

... (snip) ...

ReactDOM.render((
   ... (snip) ...
        <Route path="/repos/:userName/:repoName" component={Repo} />
   ... (snip) ...
  ),
  document.body.appendChild(document.createElement('div'))
);

Repoを表示するリンクを追加する

app/components/repos.jsxに、Repoを表示するリンクを追加します。

import React from 'react';
import ReactDOM from 'react-dom';
import { Link } from 'react-router';

export default class Repos extends React.Component {

  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <h2>Repos</h2>
        <ul>
          <li><Link to="/repos/reactjs/react-router">React Router</Link></li>
          <li><Link to="/repos/facebook/react">Facebook</Link></li>
        </ul>
      </div>
    );
  }

}

リンクをクリックすると、それぞれのrepoNameが表示されるはずです。

まとめ

  • パラメータ名は、コロンで始まる変数名を含め、Routeに割り付けてあげる。

ソースコード

github.com

React Routerのチュートリアル Lesson5

前回の続き。

hinathy.hatenablog.com

Lesson5 - Active Links

今回は、アクティブなリンクにスタイルを適用して、アクティブなリンクをわかりやすく表示してみます。

Linkにスタイルを適用する

ハイパーリンクにスタイルを与えるには、LinkコンポーネントactiveStyleというPropを設定してあげます。

import React from 'react';
import { Link } from 'react-router';

class App extends React.Component {

  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <h1>React Router Tutorial</h1>
        <ul role="nav">
          <li><Link to="/about" activeStyle={{ color: 'red' }}>About</Link></li>
          <li><Link to="/repos" activeStyle={{ color: 'red' }}>Repos</Link></li>
        </ul>
        { this.props.children }
      </div>
    );
  }
  
}

export default App;

これで、アクティブなリンクが赤色で表示されます。

別の手段でスタイルを適用する

上記とは別に、activeClassNameというPropsを使う方法もあります。 この方法は、Linkにclassを設定して、スタイルは別途スタイルシートで定義する方法です。

まずは、activeStyleactiveClassNameに置き換えます。与えるclass名はactiveにします。

import React from 'react';
import { Link } from 'react-router';

class App extends React.Component {

  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <h1>React Router Tutorial</h1>
        <ul role="nav">
          <li><Link to="/about" activeClassName="active">About</Link></li>
          <li><Link to="/repos" activeClassName="active">Repos</Link></li>
        </ul>
        { this.props.children }
      </div>
    );
  }
  
}

export default App;

次に、スタイルシートapp/index.cssを作成し、スタイルを定義します。

.active {
  color: green;
}

最後に、app/index.cssapp/index.jsxで読み込みます。

... (snip) ...

require('./index.css');

... (snip) ...

今度は、アクティブなリンクが緑色で表示されるようになります。

Linkコンポーネントのラッパーを作る

LinkにいちいちactiveClassNameを渡すのはDRYじゃないですので、LinkのラッパーコンポーネントNavLinkを作ります。

import React from 'react';
import { Link } from 'react-router';

class NavLink extends React.Component {

  render() {
    return (<Link {...this.props} activeClassName="active" />);
  }

}

export default NavLink;

NavLinkLinkを置き換えます。

import React from 'react';
import NavLink from './nav_link'

class App extends React.Component {

  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <h1>React Router Tutorial</h1>
        <ul role="nav">
          <li><NavLink to="/about">About</NavLink></li>
          <li><NavLink to="/repos">Repos</NavLink></li>
        </ul>
        { this.props.children }
      </div>
    );
  }
  
}

export default App;

まとめ

  • アクティブなリンクにスタイルを適用するには、LinkコンポーネントactiveStyleまたはactiveClassNameを使う。
  • activeStyleでは、スタイルを直接設定する。
  • activeClassNameではclass名を設定し、スタイルは別途スタイルシートで設定する。
  • Linkコンポーネントは、自身がアクティブか否かを知っているので、ラッパーを作ればactiveClassNameの指定をいちいちしなくて良い。

ソースコード

github.com

上記のリリースではNavLinkを入れ忘れたので、下記で対応。

github.com

React Routerのチュートリアル Lesson4

前回の続き。

hinathy.hatenablog.com

Lesson 4 - Nested Routes

今回は、Lesson3で追加したハイパーリンクを、全ての画面に表示します。

ReposAboutAppの子要素に配置する

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, hashHistory } from 'react-router';

require('../node_modules/bootstrap/dist/css/bootstrap.css');

import App from './components/app';
import About from './components/about';
import Repos from './components/repos';

ReactDOM.render((
    <Router history={hashHistory}>
      <Route path="/" component={App}>
        <Route path="/repos" component={Repos} />
        <Route path="/about" component={About} />
      </Route>
    </Router>
  ),
  document.body.appendChild(document.createElement('div'))
);

Appで子要素ReposAboutレンダリングする

import React from 'react';
import { Link } from 'react-router';

class App extends React.Component {

  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <h1>React Router Tutorial</h1>
        <ul role="nav">
          <li><Link to="/about">About</Link></li>
          <li><Link to="/repos">Repos</Link></li>
        </ul>
        { this.props.children }
      </div>
    );
  }
  
}

export default App;

まとめ

ソースコード

github.com