Propagation Boundary: blocking updates/fetches/effects within a scope

未关闭
#2,714 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
5/5
预计耗时
一周以上
新手友好度
35/100
Issue 类型
功能
描述清晰度
基本清楚
活跃度
冷清
技术栈
typescript

调研方向

先阅读提议的 PropagationBoundary 或 Freeze 行为以及 Owner 方案,然后跟踪在 Solid 1.0 中读取、写入、effects 和异步工作如何跨越作用域。完成的标准是达成一致的 API,并定义向下和向上的传播规则,包括是否需要 escape hatch;issue 中没有指定文件或测试。

由索引模型根据 Issue 内容生成。

描述

enhancement

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
平均合并
9 小时 56 分钟
30 天内合并 PR
487

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

solidjs/solid 的其他 Issue

查看 solidjs/solid 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。