sycamore-rs / sycamore

A library for creating reactive web apps in Rust and WebAssembly
https://sycamore-rs.netlify.app
MIT License
2.79k stars 148 forks source link

Reactivity v3! (Part 1) 🎉 #612

Closed lukechu10 closed 1 year ago

lukechu10 commented 1 year ago

A brand new implementation of Reactivity!

This gets rid of all the lifetimes introduced in #337 while maintaining the ergonomics of not having to clone everything into every closure.

Most of the API surface still has the same form, except without the lifetime annotations. A few APIs, however, need to be changed or removed all together, including create_ref and provide_context_ref. This is because these APIs internally relied on the arena allocator attached to each scope which has been removed completely. Instead, you can just use create_signal and create_context instead.

Another API that has been removed completely is RcSignal. Since Signals are 'static now, we can just include them directly in whichever state we want without any lifetime woes. However, this comes at the risk of accessing a signal beyond its "runtime-lifetime", i.e., it can still be accessed even after the scope is dropped, causing the app to immediately panic. In practice, however, these situations are rare enough that they justify the added ergonomics of removing lifetimes.

One tricky part is when collections are involved with signals. The current design of Sycamore encourages using nested signals for fine-grained updates. This, however, is no longer a good solution with the new reactive primitives. This is because if we create_signal and insert the result into, say, a Vec, the Signal will not be dropped until the enclosing Scope is dropped. Suppose we now want to remove a row from the Vec. The Signal, however, will keep on holding onto its data which essentially causes a memory leak.

Leptos solves this issue by adding a .dispose() method on Signals so that you can manually clean up after yourself when you remove Signals from a Vec. I believe, however, that this approach is error-prone and boilerplate-y. Instead, I propose introducing a new "Store-API" modeled on SolidJS's createStore primitive. This would get rid of the need all together to have nested signals. Instead, everything can be kept inside a normal Rust data-structure wrapped inside a Store which would keep track of reactive gets and sets with fine-grained updating.

Edit:

Upon further consideration, I've decided to remove the explicit reactive scope tracking with cx: Scope in favor of implicitly tracking it. This brings the function signature back to the pre-0.8 style of:

fn create_signal<T>(value: T) -> Signal<T> { ... }

instead of:

fn create_signal<T>(cx: Scope, value: T) -> &Signal<T> { ... }

which I believe is much nicer.

Remaining tasks:

codecov[bot] commented 1 year ago

Codecov Report

Patch coverage: 91.98% and project coverage change: +4.69% :tada:

Comparison is base (ddf95d8) 62.21% compared to head (8ebbeab) 66.90%.

Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #612 +/- ## ========================================== + Coverage 62.21% 66.90% +4.69% ========================================== Files 54 63 +9 Lines 9408 11168 +1760 ========================================== + Hits 5853 7472 +1619 - Misses 3555 3696 +141 ``` | [Files Changed](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs) | Coverage Δ | | |---|---|---| | [packages/sycamore-reactive3/src/context.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUtcmVhY3RpdmUzL3NyYy9jb250ZXh0LnJz) | `0.00% <0.00%> (ø)` | | | [packages/sycamore/src/web/html.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUvc3JjL3dlYi9odG1sLnJz) | `69.36% <ø> (ø)` | | | [packages/sycamore-reactive3/src/utils.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUtcmVhY3RpdmUzL3NyYy91dGlscy5ycw==) | `34.88% <34.88%> (ø)` | | | [packages/sycamore-reactive3/src/signals.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUtcmVhY3RpdmUzL3NyYy9zaWduYWxzLnJz) | `93.96% <93.96%> (ø)` | | | [packages/sycamore-reactive-macro/src/lib.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUtcmVhY3RpdmUtbWFjcm8vc3JjL2xpYi5ycw==) | `94.57% <94.57%> (ø)` | | | [packages/sycamore-reactive3/src/scope.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUtcmVhY3RpdmUzL3NyYy9zY29wZS5ycw==) | `94.96% <94.96%> (ø)` | | | [packages/sycamore-reactive3/src/memos.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUtcmVhY3RpdmUzL3NyYy9tZW1vcy5ycw==) | `97.58% <97.58%> (ø)` | | | [packages/sycamore-reactive3/src/iter.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUtcmVhY3RpdmUzL3NyYy9pdGVyLnJz) | `98.17% <98.17%> (ø)` | | | [packages/sycamore-reactive3/src/effects.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUtcmVhY3RpdmUzL3NyYy9lZmZlY3RzLnJz) | `100.00% <100.00%> (ø)` | | | [packages/sycamore-reactive3/src/store.rs](https://app.codecov.io/gh/sycamore-rs/sycamore/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sycamore-rs#diff-cGFja2FnZXMvc3ljYW1vcmUtcmVhY3RpdmUzL3NyYy9zdG9yZS5ycw==) | `100.00% <100.00%> (ø)` | |

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

lukechu10 commented 1 year ago

Hey @wingertge, I included the docs you wrote from #601 in this PR and I added you as a co-author. Hopefully you don't mind!

lukechu10 commented 1 year ago

I will be merging this now since there is already quite a significant amount of changes. The remaining work of migrating the rest of the Sycamore code base to use the new reactivity system will be done in a different PR so as to not make this one bigger than necessary.