LoginSignup
6
4

More than 5 years have passed since last update.

サーバールーティングな環境でTurbolinks 5とReactを共存させる

Last updated at Posted at 2016-09-13

@ANTON072 に指摘頂きましたが、crossroadsの扱いで、route変更時にイベントが発火しないパタンがあるので一部修正しましたm m

これはなに

SPAではない(コレなんて言えば良いんですかね?ググラビリティ低くて辛い...)普通のRailsアプリケーションで、ページごとに異なるReactのコンポーネントを描画する方法を試行してみました。

また、速度向上の影響はかなり美味しいので、Turbolinksにも対応してみました。

環境

  • rails 5
  • react 15.2.1
  • crossroads 0.12.2

crossroadsは特定のurlで起動するコンポーネントの決定および起動のために使用。
なんでも良いとは思うのですが、turbolinksがhistory apiにstateをガンガンpushするので、そこに影響が無いものが良いと思います。

つくってみる

Rails

とりあえず行き来できれば良いので、2ページ作成する

dummy_index GET    /dummy(.:format)          dummy#index
      dummy GET    /dummy/:id(.:format)      dummy#show
index.html.slim
#react-component

= link_to 'go to show page', dummy_path(1)
show.html.slim
#react-component

= link_to 'go to index page.', dummy_index_path

ReactDOMをマウントするためのidを用意しておく

React

※turbolinksに乗るので、JSファイルはwebpackとかでまとめてapplication.htmlのheaderで読み込まれること。

index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as crossroads from 'crossroads';

/* 手っ取り早くコンポーネントを2個用意しておく */
const Hello = () => {
    return (
        <p>hello</p>
    );
};

const World = () => {
    return (
        <p>world</p>
    );
};

let router;
const createRouter = () => {
    let router = crossroads.create();
    router.addRoute('/dummy', () => {
        render(<Hello />);
    });
    router.addRoute('/dummy/{id}', (id) => {
        render(<World />);
    });
    return router;
};

/* turbolinks5により、ページ遷移毎に発火 */
document.addEventListener('turbolinks:load', event => {
    const render = (dom) => {
        const target = document.querySelector('#react-component');
        ReactDOM.render(dom, target);
    };

    if (!router) {
        router = createRouter();
    }
    // こちらを実行しないと、同一ページに対するリンクの場合、crossroadsが発火しない
    router.resetState();
    router.parse(window.location.pathname + window.location.search);
});

動作確認

リンクをポチポチして遷移後、chromeにてReact Developer Toolsにて確認 

スクリーンショット 2016-09-13 17.45.33.png

コンポーネントがGCされてませんね。

対策

turbolinks#full-list-of-events

こちらのturbolinksのイベントリストを参考に、画面遷移時にコンポーネントをunmountすれば良いのでは?

ということで対策版のコードです

index.tsx
... 

document.addEventListener('turbolinks:visit', event => {
    ReactDOM.unmountComponentAtNode(
        document.querySelector('#react-component')
    );
});

最初は名前的にbefore-visitだろうと思ったのですが、historyでの遷移時には発火しないのでこちらを採用しました。

まとめ

turbolinksの恩恵を受けるためには多少ライフサイクル毎の処理を書く必要がありますね。
また、JavaScriptが初回しかロードされないので、eventListenerやtimeout系のメソッドにも気を使わないといけません。

それと、SPAではないアプリケーションにおいて、任意のページに任意のコンポーネントを起動する良いサンプルが見つからなかったのでこういう実装になりましたが、イケてる方法があったら是非教えていただきたいです!

6
4
1

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
6
4