引数付き Computed {🚀}
computed
アノテーションは、引数を取らないゲッターにのみ使用できます。引数を取る計算についてはどうでしょうか? 特定の Item
をレンダリングする React コンポーネントの以下の例を考えてみましょう。このアプリケーションは複数選択をサポートしています。
store.isSelected(item.id)
のような導出をどのように実装できるでしょうか?
import * as React from 'react'
import { observer } from 'mobx-react-lite'
const Item = observer(({ item, store }) => (
<div className={store.isSelected(item.id) ? "selected" : ""}>
{item.title}
</div>
))
これには 4 つの方法があります。以下の解決策は、この CodeSandbox で試すことができます。
computed
である必要はありません
1. 導出は MobX が追跡するために、関数を computed
としてマークする必要はありません。上記の例は、そのままでも完全に正常に動作します。computed 値は単なるキャッシュポイントであることを理解することが重要です。導出が純粋であれば (そしてそうでなければなりません)、computed
なしでゲッターまたは関数を使用しても動作は変わりません。わずかに効率が低下するだけです。
上記の例では、isSelected
が computed
ではないにもかかわらず、正常に動作します。追跡されているレンダリングの一部として関数が実行されるため、observer
コンポーネントは isSelected
によって読み取られた observable を検出して購読します。
この場合、すべての Item
コンポーネントは、選択をキャプチャする observable をすべて直接購読するため、将来の選択の変更に応答することに注意してください。これは最悪のケースの例です。一般的に、情報を導出するマークされていない関数を持つことは完全に問題ありません。そして、数値が何か他のことをすべきであることを証明するまでは、これが良いデフォルト戦略です.
2. 引数をクロージャにする
これは、元の方法よりも効率的な実装です。
import * as React from 'react'
import { computed } from 'mobx'
import { observer } from 'mobx-react-lite'
const Item = observer(({ item, store }) => {
const isSelected = computed(() => store.isSelected(item.id)).get()
return (
<div className={isSelected ? "selected" : ""}>
{item.title}
</div>
)
})
リアクションの途中で、新しい computed 値を作成します。これは正常に機能し、追加のキャッシュポイントを導入することで、すべてのコンポーネントがすべての選択変更に直接応答する必要がなくなります。このアプローチの利点は、isSelected
状態が切り替わった場合にのみコンポーネント自体が再レンダリングされることです。その場合、className
をスワップするために実際に再レンダリングする必要があります。
次のレンダリングで新しい computed
を作成するという事実は問題ありません。これはキャッシュポイントになり、前のものはきれいにクリーンアップされます。これは素晴らしく高度な最適化手法です。
3. 状態を移動する
この特定のケースでは、選択を Item
の isSelected
observable として保存することもできます。ストア内の選択は、observable ではなく computed
として表現できます: get selection() { return this.items.filter(item => item.isSelected) }
。そして、isSelected
はもう必要ありません.
4. computedFn を使用する {🚀}
最後に、mobx-utils
の computedFn
を todoStore.selected
の定義で使用して、isSelected
を自動的にメモ化できます。入力引数のすべての組み合わせの出力をメモ化する関数を作成します。
これにすぐに頼らないことをお勧めします。メモ化では、メモリ消費量について推論する前に、関数がいくつの異なる引数で呼び出されるかを考える必要があるのが一般的です。ただし、結果がリアクションによって観測されない場合はエントリを自動的にクリーンアップするため、通常の状況ではメモリリークは発生しません。
繰り返しますが、リンクされた CodeSandbox をチェックして、これを試してみてください。