state
)を管理するためのライブラリ3.1.9
ちなみに先日のReact Amsterdamでも話されてます。
なので国内では割と使い込んでる側な気がする・・?
時間が足りないので、拙ブログを読む or 6月くらいの某勉強会(予)にくる or 懇親会で話しかけてください🍕
Anything that can be derived from the application state, should be derived. Automatically.
意訳: アプリの状態に関するすべての事柄は、その状態変更に呼応して自動的に成されるべき。
view = fn(state)
図の通り、Stateを変更(Action)さえすれば、後の更新はMobXが自動的にやってくれる。
view.render()
とかconst { observable } = require('mobx');
const state = observable({
count: 1,
});
※RxとかのObservable
ではないのでご注意。
const { autorun } = require('mobx');
autorun(() => {
// いわゆる`render()`的なこと
console.log(`Count is ${state.count}!`);
});
state.count = 2;
これだけで、自動的にさっきの関数が呼ばれます。
const { autorun, observable } = require('mobx');
// state
const state = observable({
count: 1,
});
// view
autorun(() => {
console.log(`Count is ${state.count}!`); // Count is 1!
});
// action
state.count = 2; // Count is 2!
state.count = 3; // Count is 3!
おどろきの簡単さ・・!
mobx.observable()
表示を更新したいから値を更新する、その思考の流れがそのままコードになるのが重要。
class Store {
constructor() {
extendObservable(this, {
items: [],
}),
}
}
or
class Store {
@observable items = [];
}
Decoratorsの使用はマストではないが、使えるなら使いたくなると思う・・!
class Ticket {
@observable price = 1000;
@observable amount = 1;
@computed get total() {
return this.price * this.amount;
}
}
const ticket = new Ticket();
console.log(ticket.total); // 1000
ticket.amount = 2;
console.log(ticket.total); // 2000
特定の状態に由来する状態をcomputed
で宣言的に。
const React = require('react');
const { observer } = require('mobx-react');
const View = ({ todoStore }) => (
<div>
残TODO数: {todoStore.unfinishedCount}
<ul>
{ todoStore.items.map(todo => (
<li key={todo.id}
<Todo item={todo} />
</li>
)) }
</ul>
</div>
);
// いるのココだけ
module.exports = observer(View);
mobx-react
という別パッケージがあり、observer
はさっきのautorun
のラッパー。
必要なStateに更新があった時だけ、自動でrender()
されるように。
// for rendering views
ReactDOM.render(<View todoStore={store} />, $el);
// for others
reaction(
() => store.unfinishedCount,
count => {
if (count > 10) {
askColleagueToHelp(store.items);
}
if (count > 100) {
reportToBoss(store.items);
}
}
)
observable
な値の変化に対するreaction
を指定することで、Viewの表示に関することも、それ以外のことも、State由来の事柄は全てを宣言的・リアクティブに書ける。
const { useStrict, action } = requre('mobx');
useStrict(true);
state.count = 10; // Error!
const modCount = action(() => {
state.count = 10;
});
modCount(); // Ok!
action()
でラップすることで、その関数からしかStateが変更できないようにできる。
uiStore.setLoading(true);
fetch('/api/my/todos/')
.then(items => {
todoStore.init(items);
uiStore.setLoading(false);
})
.catch(err => {
uiStore.showError(err);
});
非同期だからといって特殊なことをする必要はない。 直感的に、ただStateを更新するだけ。
// Store(State + Action)
class TodoStore {
@observable items = [];
@action
addTodo(title) {
this.items.push(new Todo(title));
}
}
<!-- View -->
<input type="text" /><button onClick={() => addTodo($input.value)}>ADD</button>
// Event
function addToto(title) {
if (title.trim().length === 0) { return; }
store.addTodo(title);
}
※コードはイメージです
ちょうどいい上に賢い・・これが魔法・・
○○uxのActionオブジェクトは未だに謎・・。一方向フローは良いとしてもtype
て・・タイムトラベルとかしねーし・・
Flux一派に疲れた貴方に送るリーズナブルな選択肢として。
Single source of truth
したいんですが?ライブラリは必要になって初めていれるもの。
何かあればメンションください or この後の🍕で!