Posted at

yahoo/fluxible関連

More than 3 years have passed since last update.

https://github.com/yahoo/fluxible

http://fluxible.io/

まだ自分用のメモ書きレベルです…

公式サイトに乗っている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?/'

ComponentconnectToStoreに追記する。重要箇所に☆マークつけた。

// 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-routercreateNavLinkComponentは、引数にオブジェクトを渡して呼び出すと拡張できる。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
};
}
);

https://github.com/yahoo/fluxible-router/blob/master/docs/api/navigateAction.md


トラブルシューティング

すこし「ん?」って思ったところを挙げます。


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