まだ自分用のメモ書きレベルです…
公式サイトに乗っているQuickStartで作ったプロジェクトを前提に書きます。まだの人は、次のコマンドを打ってください。Windowsの方は後述のTipsに有るスクリプトを定義しないと動かないです。
npm i -g yo generator-fluxible
yo fluxible
npm run dev
なお、yoのテンプレートの依存パッケージが古くてうまく動かないことがあるので、最低babelくらいはアップデートしておきましょう。
ディレクトリ構成
- actions
- アクションの定義 (yo fluxibleでは入らないものの作ると便利)
- components
- Viewの定義
- stores
- ストアの定義
- build
- webpackの出力ディレクトリ(IDEなど使う場合は除外する)
- configs
- ルーティングなどの定義
使用されている(学ぶべき)技術
- Babel (ECMA Script6)
- ReactJS
- Webpack
- ES Lint
Tips
windowsで'npm run dev'を動かす
標準が&
使ったワンライナーなので、Linux系でしか動かない。
package.jsonに以下のスクリプトを定義して、npm run dev-win
を叩く。
Grup使ってもOK!
{
"scripts": {
"dev-win": "start node webpack-dev-server.js & SET PORT=3001 & nodemon start.js -e js,jsx"
}
}
ウザいブラウザのログの消し方
ブラウザのコンソールから
localStorage.debug = "" // OFF
localStorage.debug = "*" // ON
npm debug
を使用しているので、その他のオプションはここから探せる気がする。
https://www.npmjs.com/package/debug
前に書いた記事: http://qiita.com/kamijin_fanta/items/55795bd3936cc3a7fc5a
クライアントだけで描写したい
server.jsから編集。
-
executeAction(navigateAction…
とかは見ての通り、URLでのナビゲーションを行っているだけなのでサーバ側には不要。 - stateをdehydrateして取得する必要も渡す必要もない無い。
- markupもコンポーネントをブラウザに書かせればいいので不要。
// server.js
server.use((req, res, next) => {
let context = app.createContext();
debug('Rendering Application component into html');
const html = React.renderToStaticMarkup(htmlComponent({
clientFile: env === 'production' ? 'main.min.js' : 'main.js',
context: context.getComponentContext(),
// state: exposed,
// markup: React.renderToString(createElementWithContext(context))
}));
debug('Sending markup');
res.type('html');
res.write('<!DOCTYPE html>' + html);
res.end();
});
server.jsでやっていたnavigateActionをブラウザでやるだけ。urlにはlocation.pathname + location.search
を指定することによってルーティングできる。
// client.js
import {navigateAction} from 'fluxible-router';
let context = app.createContext();
context.getActionContext().executeAction(navigateAction, {
url: location.pathname + location.search
}, (err) => {
if (err) {
if (err.statusCode && err.statusCode === 404) {
next();
} else {
next(err);
}
return;
}
window.context = context;
const mountNode = document.getElementById('app');
debugClient('React Rendering');
React.render(
createElementWithContext(context),
mountNode,
() => debugClient('React Rendered')
);
});
RouteとNavLink
あってもなくても良いURLパラメータの指定
// routes.js
home: {
path: '/:id?/',
},
<NavLink routeName="home" navParams={{id: null}}>Home</NavLink>
<NavLink routeName="home" navParams={{id: 123}}>Home 123</NavLink>
最後の/が無いとパラメータ無指定時に落ちる。ちなみに、routes.js
を編集した時はサーバごと再起動させたほうが良い。
パスのコンパイルの詳しい挙動は https://github.com/pillarjs/path-to-regexp/ のテストを見たら分かった。
URLパラメータを取得する
ルーティングの設定の任意のpathにパラメータの名前を指定する。
// routes.js
path: '/member/:user?/'
Component
のconnectToStore
に追記する。重要箇所に☆マークつけた。
// components/ComponentName.js
export default connectToStores(
ComponentName,
[ApplicationStore, "RouteStore"], // ☆
function (context, props) {
var appStore = context.getStore(ApplicationStore);
var routeStore = context.getStore("RouteStore"); // ☆
var params = routeStore.getCurrentRoute().get("params").toObject(); // ☆
return {
params: params, // ☆
user: params.user
};
}
);
コンポーネントからthis.props.params
で取得できるようになる。
クエリを含んだURLをNavLinkで生成する
結論から言うと、用意されていない。
Case1 makePath
コンポーネント上でthis.props.context.makePath(route, parm)
を使用することによってURLを取得できる。それに+"?key=value"
のような感じでクエリ付きのURLを生成し、JSXによりa
タグを生成する。
Case2 Extend NavLink
fluxible-router
のcreateNavLinkComponent
は、引数にオブジェクトを渡して呼び出すと拡張できる。1行目でクエリ文字を生成し、最後の行で結合させる。
// library/QueryNavLink.js
import { createNavLinkComponent } from 'fluxible-router';
import querystring from 'querystring';
export default createNavLinkComponent({
_getHrefFromProps: function (props) {
var query = props.query==undefined?"":"?" + querystring.stringify(props.query);
var href = props.href;
var routeName = props.routeName;
var routeStore = this.context.getStore("RouteStore");
if (!href && routeName) {
href = routeStore.makePath(routeName, props.navParams);
}
if (!href) {
throw new Error('NavLink created without href or unresolvable routeName \'' + routeName + '\'');
}
return href + query;
}
});
あとは普通に使うときにqueryパラメータをつけるだけ。
// components/ComponentName.js
import NavLink from '../library/QueryNavLink';
<NavLink routeName="mail" query={{page: 1}}>テキスト</NavLink>
アクセスは、Route Dataのqueryを見るだけ。一応公式ドキュメントにも記述の通り、複数の値が見つかった場合は、配列として格納される点を注意。
export default connectToStores(
Foo,
["RouteStore"],
function (context, props) {
var routeStore = context.getStore("RouteStore");
var query = routeStore.getCurrentRoute().get("query").toObject()
return {
query: query
};
}
);
トラブルシューティング
すこし「ん?」って思ったところを挙げます。
this.context.executeAction is not a function
使用すると明示的に指定したComponentでしかexecuteActionは使えない。
// components/ComponentName.js
ComponentName.contextTypes = {
executeAction: React.PropTypes.func.isRequired
}
Store undefined was not registered.
app.jsでStoreを登録しないと使えない。
// app.js
app.registerStore(TestStore);
Cannot read property 'storeName' of undefined
app.js
のRouteStoreの読み込み位置を変える。Webpackの都合なのか、数回ハマった。
// app.js
import RouteStore from './stores/RouteStore'; // Application,ApplicationStoreより上に持っていくる
import Application from './components/Application';
import ApplicationStore from './stores/ApplicationStore';
第二引数のストアは、名前でも渡せるのでそっちのほうが良いかも。文字列で渡すと表題のエラーは起こらない。特にRouteStore
関係でエラーが出るのでその辺は注意が必要。
export default connectToStores(
Application,
["ApplicationStore"],
function (context, props) {
// code
}
);