このコードは、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
の場合は即時レンダリングされます。
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
関数を呼び出します。
ClientStartup.clientStartup(ctx);
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の処理は挟まないです。