Implement effects using reactivecontext

This commit is contained in:
Jonathan Kelley committed 2024-02-01 13:21:52 -08:00
1 parent 968f24a7b3
commit 7c2947a131
21 files changed
+287 -320

No files matched your search

+9
View File
@@ -111,6 +111,11 @@ pub fn needs_update() {
Runtime::with_current_scope(|cx| cx.needs_update());
}
/// Mark the current scope as dirty, causing it to re-render
pub fn needs_update_any(id: ScopeId) {
Runtime::with_current_scope(|cx| cx.needs_update_any(id));
}
/// Schedule an update for the current component
///
/// Note: Unlike [`needs_update`], the function returned by this method will work outside of the dioxus runtime.
@@ -252,6 +257,10 @@ pub fn after_render(f: impl FnMut() + 'static) {
/// Effects rely on this to ensure that they only run effects after the DOM has been updated. Without flush_sync effects
/// are run immediately before diffing the DOM, which causes all sorts of out-of-sync weirdness.
pub async fn flush_sync() {
// if flushing() {
// return;
// }
_ = Runtime::with(|rt| rt.flush.clone())
.unwrap()
.recv_async()
+8 -8
View File
@@ -88,13 +88,13 @@ pub use crate::innerlude::{
pub mod prelude {
pub use crate::innerlude::{
consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, flush_sync,
generation, has_context, needs_update, parent_scope, provide_context, provide_root_context,
remove_future, schedule_update, schedule_update_any, spawn, spawn_forever, suspend,
try_consume_context, use_after_render, use_before_render, use_drop, use_error_boundary,
use_hook, use_hook_with_cleanup, AnyValue, Attribute, Component, ComponentFunction,
Element, ErrorBoundary, Event, EventHandler, Fragment, HasAttributes, IntoAttributeValue,
IntoDynNode, OptionStringFromMarker, Properties, Runtime, RuntimeGuard, ScopeId,
ScopeState, SuperFrom, SuperInto, Task, Template, TemplateAttribute, TemplateNode, Throw,
VNode, VNodeInner, VirtualDom,
generation, has_context, needs_update, needs_update_any, parent_scope, provide_context,
provide_root_context, remove_future, schedule_update, schedule_update_any, spawn,
spawn_forever, suspend, try_consume_context, use_after_render, use_before_render, use_drop,
use_error_boundary, use_hook, use_hook_with_cleanup, AnyValue, Attribute, Component,
ComponentFunction, Element, ErrorBoundary, Event, EventHandler, Fragment, HasAttributes,
IntoAttributeValue, IntoDynNode, OptionStringFromMarker, Properties, Runtime, RuntimeGuard,
ScopeId, ScopeState, SuperFrom, SuperInto, Task, Template, TemplateAttribute, TemplateNode,
Throw, VNode, VNodeInner, VirtualDom,
};
}
+6 -1
View File
@@ -60,8 +60,13 @@ impl Scope {
/// Mark this scope as dirty, and schedule a render for it.
pub fn needs_update(&self) {
self.needs_update_any(self.id)
}
/// Mark this scope as dirty, and schedule a render for it.
pub fn needs_update_any(&self, id: ScopeId) {
self.sender()
.unbounded_send(SchedulerMsg::Immediate(self.id))
.unbounded_send(SchedulerMsg::Immediate(id))
.expect("Scheduler to exist if scope exists");
}
+12 -8
View File
@@ -372,11 +372,15 @@ impl VirtualDom {
///
/// Whenever the Runtime "works", it will re-render this scope
pub fn mark_dirty(&mut self, id: ScopeId) {
if let Some(context) = self.runtime.get_state(id) {
let height = context.height();
tracing::trace!("Marking scope {:?} ({}) as dirty", id, context.name);
self.dirty_scopes.insert(DirtyScope { height, id });
}
let Some(scope) = self.runtime.get_state(id) else {
return;
};
tracing::trace!("Marking scope {:?} ({}) as dirty", id, scope.name);
self.dirty_scopes.insert(DirtyScope {
height: scope.height(),
id,
});
}
/// Call a listener inside the VirtualDom with data from outside the VirtualDom. **The ElementId passed in must be the id of an element with a listener, not a static node or a text node.**
@@ -422,9 +426,6 @@ impl VirtualDom {
/// let dom = VirtualDom::new(app);
/// ```
pub async fn wait_for_work(&mut self) {
// Send the flush signal to the runtime
_ = self.flush_tx.try_send(());
// And then poll the futures
self.poll_tasks().await;
}
@@ -432,6 +433,9 @@ impl VirtualDom {
/// Poll futures without progressing any futures from the flush table
async fn poll_tasks(&mut self) {
loop {
// Send the flush signal to the runtime, waking up tasks
_ = self.flush_tx.try_send(());
// Process all events - Scopes are marked dirty, etc
// Sometimes when wakers fire we get a slew of updates at once, so its important that we drain this completely
self.process_events();