脱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');
});