ReactOnRailsのしくみ

このコードは、ReactとReduxを使用するReact on Rails環境でのReduxストアの登録と初期データの挿入（hydration）のプロセスを示しています。それぞれのステップを詳しく説明します。

## 作成したstoreGeneratorを登録(registeredStoreGenerators配列に追加)

```
  register(storeGenerators: { [id: string]: Store }): void {
    Object.keys(storeGenerators).forEach(name => {
      if (registeredStoreGenerators.has(name)) {
        console.warn('Called registerStore for store that is already registered', name);
      }

      const store = storeGenerators[name];
      if (!store) {
        throw new Error('Called ReactOnRails.registerStores with a null or undefined as a value ' +
          `for the store generator with key ${name}.`);
      }

      registeredStoreGenerators.set(name, store);
    });
  },
```

## 初期データの準備 (Hydration)

`redux_store` ヘルパーメソッドは、サーバーサイドでのレンダリング時にReduxストアの初期データをセットアップします。このメソッドは、ストアの名前と初期プロパティを受け取り、それらを用いてクライアントサイドで利用するためのデータを生成します。`defer` 引数が `true` である場合、ストアのデータは後でレンダリングされることを意味し、`false` の場合は即時レンダリングされます。

```react_on_rails/lib/react_on_rails/helper.rb
    def redux_store(store_name, props: {}, defer: false)
      # ... redux_storeのデータ構築 ...
      redux_store_data = { store_name: store_name,
                           props: props }
      if defer
        @registered_stores_defer_render ||= []
        @registered_stores_defer_render << redux_store_data
        "YOU SHOULD NOT SEE THIS ON YOUR VIEW -- Uses as a code block, like <% redux_store %> " \
          "and not <%= redux store %>"
      else
        @registered_stores ||= []
        @registered_stores << redux_store_data
        result = render_redux_store_data(redux_store_data)
        prepend_render_rails_context(result)
      end
    end

    def redux_store_hydration_data
      # ... デファードされたストアのデータを結合し、HTMLに安全な形でレンダリング ...
      return if @registered_stores_defer_render.blank?

      @registered_stores_defer_render.reduce(+"") do |accum, redux_store_data|
        accum << render_redux_store_data(redux_store_data)
      end.html_safe
    end

    def render_redux_store_data(redux_store_data)
      # ... redux_storeデータをHTMLのscriptタグとしてレンダリング ...
      result = content_tag(:script,
                           json_safe_and_pretty(redux_store_data[:props]).html_safe,
                           type: "application/json",
                           "data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe)

      prepend_render_rails_context(result)
    end
```

### クライアントサイドでのストアの初期化(storeの作成と準備した初期データの登録)

クライアントサイドでは、`clientStartup` 関数がページの準備ができた時に呼び出されます。この関数は、サーバーサイドから送られた初期データを元に、対応するReduxストアを初期化するための `initializeStore` 関数を呼び出します。


```node_package/src/ReactOnRails.ts
ClientStartup.clientStartup(ctx);
```

```node_package/src/clientStartup.ts
export function clientStartup(context: Context): void {
  // ... ページ準備完了時にレンダリングを初期化 ...
  // 省略
  onPageReady(renderInit);
  // =>なんやかんやあってinitializeStoreが呼び出される
}

// REACT_ON_RAILS_STORE_ATTRIBUTEは<%= redux_store_hydration_data %>で生成されるcontent_tagのkey
function initializeStore(el: Element, context: Context, railsContext: RailsContext): void {
  // ... 指定されたストアのデータを取得し、ストアを初期化 ...
  const name = el.getAttribute(REACT_ON_RAILS_STORE_ATTRIBUTE) || '';
  const props = (el.textContent !== null) ? JSON.parse(el.textContent) : {};
  const storeGenerator = context.ReactOnRails.getStoreGenerator(name);
  const store = storeGenerator(props, railsContext);
  context.ReactOnRails.setStore(name, store);
}
```

解説

```
const props = (el.textContent !== null) ? JSON.parse(el.textContent) : {};
```
はhydratedStores からinitial_dataを取得する。

```
const storeGenerator = context.ReactOnRails.getStoreGenerator(name);
```
はregisteredStoreGenerators からgenerator関数を取得する。

```
const store = storeGenerator(props, railsContext);
```

でregisteredStoreGenerators関数を使用して新しいstoreを生成。

```
context.ReactOnRails.setStore(name, store);
```
でhydratedStoresを更新。

アプリケーションでstore = getStore(store_name) を使用して取得するのはhydratedStores に入っている初期化済みのstoreです。
storeの更新は通常のreduxに戻るため特にreact_on_railsの処理は挟まないです。