es6 の module import/export の仕様を再確認したかったのが出発点。
準備
手軽なのでcreate-react-appを利用する。
create-react-app mystore
cd mystore
store実装
store.js
export let globalState = { count: 1 }
export const increment = () => {
globalState = { count: globalState.count + 1 }
}
変数とその変更方法を提供してるだけ
これを複数箇所からimportして利用する。
App.js
import React from 'react';
import './App.css';
import { globalState, increment } from './store'
import Counter from './Counter'
class App extends React.Component {
state = { count: globalState.count }
increment = () => {
increment()
this.setState({ count: globalState.count })
}
render() {
return (
<div className="App">
<header className="App-header">
<h2>Parent counter</h2>
<p>{globalState.count}</p>
<button onClick={this.increment}>increment</button>
<Counter />
</header>
</div>
);
}
}
export default App;
component の stateを利用してないのはご愛嬌。
(値を直接見たかったので。再描画用のsetState)。
Counter.js
import React from 'react'
import { globalState, increment } from './store'
class Counter extends React.Component {
state = { count: globalState.count }
increment = () => {
increment()
this.setState({ count: globalState.count })
}
render() {
return (
<div>
<h2>Child counter</h2>
<p>{globalState.count}</p>
<button onClick={this.increment}>increment</button>
</div>
)
}
}
export default Counter
ポイント:
- export されている値が変更されたら、その変更はimport先のファイルをまたいで維持される(この例でも、リアルタイムに描画されるかはともかく、親と子で最終的には値が同期されている)
- 調べると ES6 export では binding がエクスポートされるとか書かれてるがよくわからない
- その具体例と思われるシングルインスタンスのエクスポートと関連づけた方が理解しやすい
- リアルタイムに再描画されない場合がある
- この例では子での変更は親に反映されない
- 親の方のボタンをクリックしたら同期される
subscription 機能を実装
値をリアルタイムに同期させたいなら起点となるイベントが必要だということだろう。
subscription用のコールバックを格納する配列を作って変更のたびに全て実行する。
store.js
const subscriptions = []
export const addSubscription = (callback) => {
subscriptions.push(callback)
}
export let globalState = { count: 1 }
export const subscribedIncrement = () => {
globalState = { count: globalState.count + 1 }
subscriptions.forEach(callback => callback(globalState))
// console.log(subscriptions)
}
コンポーネントのコンストラクタあたりでコールバック追加すれば良い。
App.js
// ...
class App extends React.Component {
constructor(props) {
super(props)
this.state = { count: globalState.count }
addSubscription(this.onGlobalStateChange)
}
increment = () => {
subscribedIncrement()
this.setState({ count: globalState.count })
}
onGlobalStateChange = (globalState) => {
this.setState(globalState)
}
// ...
解除方法は知らぬ。
結論
- Reduxで良い
- ただし、React Reduxは何が嬉しいのだったか頭を整理中
- propsに渡すパターン?
- コンテナコンポーネント作るならいらん?