LoginSignup
4
4

More than 5 years have passed since last update.

脱react-routerしたい人の0.5歩目くらいのもの

Last updated at Posted at 2016-10-19

脱react-routerしたい人の0.5歩目くらいのもの

https://medium.freecodecamp.com/you-might-not-need-react-router-38673620f3d#.5pzh5miki
の記事を見たり、
https://github.com/kriasoft/react-static-boilerplate
なんかを見たものの、
webpack, reduxが前提だったりhistoryパッケージのバージョンが古かったりして、うがーとなったのでもう少し簡素にうごくもの作ってみた。
脱react-routerしたい理由は特に触れない。

packages.json, .babelrcはだいたいこんな感じ。
https://github.com/mjackson/history
を使う。

packages.json
{
  "private": true,
  "scripts": {
    "build": "browserify -t babelify ./path/to/src/root.js -o ./path/to/public/js/bundle.js"
 },
  "dependencies": {
    "babel-plugin-transform-object-rest-spread": "^6.8.0",
    "babel-preset-es2015": "^6.13.2",
    "babel-preset-react": "^6.11.1",
    "babelify": "^7.3.0",
    "history": "^4.2.0",
    "react": "^15.3.0",
    "react-dom": "^15.3.0"
  }
}

{
  "presets": ["react", "es2015"],
  "plugins": ["transform-object-rest-spread"]
}

最上位の親コンポーネントをRootとして定義。
ページの内容はIndex, Subとして定義。
pathが'/index'ならIndexを表示・
'/subならSubを表示する。
Rootコンポーネント上に各ページへのリンクを配置。

root.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import createBrowserHistory from 'history/createBrowserHistory';

// historyオブジェクトを作成。
let history = createBrowserHistory();

class Root extends Component {
  constructor() {
    super();
    this.state = {
      key1: 'foo',
      key2: 'bar',
    }
  }
  // ページ遷移を行う
  transition(e) {
    e.preventDefault();
    history.push(e.currentTarget.pathname);
  }
  render() {
    // React.cloneElement()を使って子コンポーネントにstateをpropsとして渡す。
    return (
      <div>
      <ul>
        <li>
          <a href="/" onClick={(e) => this.transition(e)}>Index</a>
        </li>
        <li>
          <a href="/sub" onClick={(e) => this.transition(e)}>Sub</a>
        </li>
      </ul>
      {React.cloneElement(this.props.children, { ...this.state })}
      </div>
    );
  }
}

// 各ページ用子コンポーネントの定義
const Index = (props) => {
  return (
    <div>
      {props.key1}<br />
      これはIndexページ
    </div>
  );
}

const Sub = (props) => {
  return (
    <div>
      {props.key2}<br />
      これはSubページ
    </div>
  );
}

// パスとコンポーネントの紐付け定義
const routes = [
  { path: '/', action: () => <Index />},
  { path: '/sub', action: () => <Sub />}
]

const root = document.getElementById('root');

// パスに応じた子コンポーネントとともにrenderする。
// 実際は正規表現を使用してもっといい感じにするところ。

function render(location) {
  for (let route of routes) {
    if(location.pathname === route.path) {
      const content = route.action();
      ReactDOM.render(
        <Root>{content}</Root>,
        root
      );
      return;
    }
  }
}

// historyを監視、パスが変化したときに引数のコールバックを実行して、contentを更新する。
// 第二引数の使い方は後で調べる
history.listen((location, action) => {
  render(location);
});
// ページ読み込み時に最初に実行される
render(history.location);

subページのパスに直接アクセスされた場合など、何もしなければnot foundになってしまうので、サーバーサイドが例えばLaraval5.3であれば以下のようにしてアクセスをrootパスのハンドラーに集約する必要がある。

routes/web.php
Route::get('/{action?}', function () {
    return view('root');
});
4
4
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
4
4