Propagation Boundary: blocking updates/fetches/effects within a scope
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 35/100
- issue の種類
- 機能追加
- 明瞭さ
- おおむね明確
- 活発さ
- 静か
- 技術スタック
- typescript
- 領域
- frontend, performance
調査の方向性
まず、提案されている PropagationBoundary または Freeze の挙動と Owner の代替案を読み、次に Solid 1.0 で読み取り、書き込み、エフェクト、非同期処理がスコープをまたいでどのように伝播するかを追跡します。完了の条件は、合意された API と、下方向および上方向の伝播に関する定義済みのルールがあり、エスケープハッチが必要かどうかも含まれていることです。この issue ではファイルやテストは指定されていません。
索引モデルが issue の本文から書いたものです。
説明
This is an enhancement issue following this Discord conversation between Ryan and myself.
Usecase
I'm trying to write a router that is as fast for SPAs as the native browser history is for old-fashioned MPAs, especially when a user goes back in their history. For this I want to keep previous pages in memory, including their DOM nodes. However, while they are in an invisible/cached state, I don't want them to trigger fetches etc.
For example, imagine a shopping site. The current "shopping cart" is essentially global state. I visit a page /details that shows me price details of my shopping cart. Part of its implementation behaves somewhat like this:
<For each={shoppingCart()}>
{(item) => <Item price={fetchCurrentPriceForItem(item().id)} name={item().name}/>}
</For>
I then want to add something else to my shopping cart so I navigate to /search?q=something+else. I add it to my shopping cart. Now the shoppingCart() signal updates in the cached /details page, and fetches pricing details I didn't need fetching for. There may also be effects running that are wasting CPU.
My example is very simplified but I had variations of this problem in "real" code (using Solid 1.0) too. I ended up defining a isPageVisible() signal that so any refetch only happens when it is true, but then you have to wire that signal through all your effects. This also only solves the issue for effects/resources, but Solid still "wastes" CPU cycles updating other parts of the DOM.
Proposed solution
A minimal API:
<PropagationBoundary block={isPageVisible()}>
...
</PropagationBoundary>
When block == true, all elements nested within the PropagationBoundary will no longer update, run any effects or do any async stuff. Alternatively we can name it Freeze to signal that its contents will be frozen regardless of state changes.
In the Discord thread we also talked about more exotic extra options, for example only running effects on animationFrame. I'm currently not sure how that would fit within this concept of a propagation boundary.
I'd also be fine with this being an optional argument block: () => boolean when creating an Owner. It may simplify the implementation, and I'm creating an Owner for each page anyway.
Considerations
Updates flowing "upwards"
Coming back to the example, shoppingCart is "top-level" state and its updates travel "down" to the page and eventually its <Item/> components. If at any point an active propagation boundary is encountered, updates shouldn't propagate. Conceptually quite simple.
However, an update may also trigger "within" a propagation boundary and travel "upward". For example: the /details page somehow triggers an update to a signal that is displayed in the header of the page. This should naturally occur less frequently, but we should clearly define what happens anyway: do we block it or not? Personally I think there are arguments for both cases.
Intuitively I'd expect it is easiest to ignore the source of the update/write. The rule would be simple then: if a read happens inside an active propagation boundary, it will never see new state. This intuition is however based on Solid 1.0, so take it with a grain of salt.
Escape hatch
I proposed a withoutBoundary helper to Ryan that would unblock propagation while in an active propagation boundary. I currently don't see a need for it anymore, unless the behaviour for "upwards" updates becomes more complex than what I suggested above.
Alternative name: "Freeze"
Maybe PropagationBoundary is rather technical, and it's better to describe the resulting behaviour rather than the way it is implemented. In that case I'd suggest Freeze, as it "freezes" the contents and any associated effects. Happy to hear your thoughts.
- 主要言語
- TypeScript
- スター
- 36.2k
- フォーク
- 1.1k
- 平均マージ
- 11時間 5分
- マージ済み PR(30日)
- 475
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
solidjs/solid のほかの issue
-
難易度 4/5 3〜5日 初心者へのやさしさ 28/100
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 12/100
メンテナーはふだん 1 日以内に返信
-
[2.0 / next] <Errored> recovery shows a pending memo's answer to an older input beside the new inputオープン
難易度 4/5 3〜5日 初心者へのやさしさ 20/100
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 15/100
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 18/100
メンテナーはふだん 1 日以内に返信
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
kunchenguid/gh-axi#247 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
lidge-jun/opencodex#8101 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
effort:S P3 refactor
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
nightscout/nocturne#3243 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
JuliusBrussee/caveman#1449 ·
メンテナーはふだん 1 日以内に返信