wip: continue to tweak suspense
This commit is contained in:
1 parent
08ca068d1c
commit
7e6cea3a13
9 files changed
+90
-84
No files matched your search
+4
-1
@@ -63,7 +63,10 @@ async fn breed_pic(cx: Scope, breed: String) -> Element {
|
|||||||
Ok(resp) => render! {
|
Ok(resp) => render! {
|
||||||
div {
|
div {
|
||||||
button {
|
button {
|
||||||
onclick: move |_| fut.restart(),
|
onclick: move |_| {
|
||||||
|
println!("clicked");
|
||||||
|
fut.restart()
|
||||||
|
},
|
||||||
"Click to fetch another doggo"
|
"Click to fetch another doggo"
|
||||||
}
|
}
|
||||||
img {
|
img {
|
||||||
|
|||||||
@@ -22,14 +22,6 @@ pub(crate) struct VProps<'a, P, A, F: ComponentReturn<'a, A> = Element<'a>> {
|
|||||||
_marker: PhantomData<A>,
|
_marker: PhantomData<A>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PropsAllocation<P> {
|
|
||||||
/// If it's memoized, then the heap owns the props
|
|
||||||
Memoized(*mut P),
|
|
||||||
|
|
||||||
/// If it's borrowed, then the parent owns the props
|
|
||||||
Borrowed(P),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, P, A, F> VProps<'a, P, A, F>
|
impl<'a, P, A, F> VProps<'a, P, A, F>
|
||||||
where
|
where
|
||||||
F: ComponentReturn<'a, A>,
|
F: ComponentReturn<'a, A>,
|
||||||
|
|||||||
+38
-65
@@ -19,18 +19,10 @@ impl VirtualDom {
|
|||||||
mutations: &mut Mutations<'a>,
|
mutations: &mut Mutations<'a>,
|
||||||
template: &'a VNode<'a>,
|
template: &'a VNode<'a>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let mutations_to_this_point = mutations.len();
|
|
||||||
|
|
||||||
self.scope_stack.push(scope);
|
self.scope_stack.push(scope);
|
||||||
let out = self.create(mutations, template);
|
let out = self.create(mutations, template);
|
||||||
self.scope_stack.pop();
|
self.scope_stack.pop();
|
||||||
|
|
||||||
if !self.collected_leaves.is_empty() {
|
|
||||||
if let Some(boundary) = self.scopes[scope.0].has_context::<SuspenseContext>() {
|
|
||||||
println!("Boundary detected and pending leaves!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,98 +330,79 @@ impl VirtualDom {
|
|||||||
use RenderReturn::*;
|
use RenderReturn::*;
|
||||||
|
|
||||||
match return_nodes {
|
match return_nodes {
|
||||||
Sync(Some(t)) => self.create_component(mutations, scope, t, idx, component),
|
Sync(Some(t)) => self.mount_component(mutations, scope, t, idx),
|
||||||
Sync(None) | Async(_) => {
|
Sync(None) | Async(_) => {
|
||||||
self.create_component_placeholder(template, idx, component, scope, mutations)
|
self.mount_component_placeholder(template, idx, scope, mutations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_component<'a>(
|
fn mount_component<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mutations: &mut Mutations<'a>,
|
mutations: &mut Mutations<'a>,
|
||||||
scope: ScopeId,
|
scope: ScopeId,
|
||||||
template: &'a VNode<'a>,
|
template: &'a VNode<'a>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
component: &'a VComponent<'a>,
|
|
||||||
) -> usize {
|
) -> usize {
|
||||||
// // Keep track of how many mutations are in the buffer in case we need to split them out if a suspense boundary
|
// Keep track of how many mutations are in the buffer in case we need to split them out if a suspense boundary
|
||||||
// // is encountered
|
// is encountered
|
||||||
// let mutations_to_this_point = mutations.len();
|
let mutations_to_this_point = mutations.len();
|
||||||
|
|
||||||
// Create the component's root element
|
// Create the component's root element
|
||||||
let created = self.create_scope(scope, mutations, template);
|
let created = self.create_scope(scope, mutations, template);
|
||||||
|
|
||||||
if !self.collected_leaves.is_empty() {
|
// If there are no suspense leaves below us, then just don't bother checking anything suspense related
|
||||||
println!("collected leaves: {:?}", self.collected_leaves);
|
if self.collected_leaves.is_empty() {
|
||||||
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
created
|
// If running the scope has collected some leaves and *this* component is a boundary, then handle the suspense
|
||||||
|
let boundary = match self.scopes[scope.0].has_context::<SuspenseContext>() {
|
||||||
|
Some(boundary) => boundary,
|
||||||
|
_ => return created,
|
||||||
|
};
|
||||||
|
|
||||||
// // If running the scope has collected some leaves and *this* component is a boundary, then handle the suspense
|
// Since this is a boundary, use its placeholder within the template as the placeholder for the suspense tree
|
||||||
// let boundary = match self.scopes[scope.0].has_context::<SuspenseContext>() {
|
let new_id = self.next_element(template, template.template.node_paths[idx]);
|
||||||
// Some(boundary) if !self.collected_leaves.is_empty() => boundary,
|
|
||||||
// _ => return created,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Since this is a boundary, use it as a placeholder
|
// Now connect everything to the boundary
|
||||||
// let new_id = self.next_element(template, template.template.node_paths[idx]);
|
self.scopes[scope.0].placeholder.set(Some(new_id));
|
||||||
// component.placeholder.set(Some(new_id));
|
|
||||||
// self.scopes[scope.0].placeholder.set(Some(new_id));
|
|
||||||
// mutations.push(AssignId {
|
|
||||||
// id: new_id,
|
|
||||||
// path: &template.template.node_paths[idx][1..],
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Now connect everything to the boundary
|
// This involves breaking off the mutations to this point, and then creating a new placeholder for the boundary
|
||||||
// let boundary_mut = boundary;
|
// Note that we break off dynamic mutations only - since static mutations aren't rendered immediately
|
||||||
// let split_off = mutations.split_off(mutations_to_this_point);
|
let split_off = unsafe {
|
||||||
// let split_off: Vec<Mutation> = unsafe { std::mem::transmute(split_off) };
|
std::mem::transmute::<Vec<Mutation>, Vec<Mutation>>(
|
||||||
|
mutations.split_off(mutations_to_this_point),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
boundary.mutations.borrow_mut().edits.extend(split_off);
|
||||||
|
boundary.created_on_stack.set(created);
|
||||||
|
boundary
|
||||||
|
.waiting_on
|
||||||
|
.borrow_mut()
|
||||||
|
.extend(self.collected_leaves.drain(..));
|
||||||
|
|
||||||
// if boundary_mut.placeholder.get().is_none() {
|
// Now assign the placeholder in the DOM
|
||||||
// boundary_mut.placeholder.set(Some(new_id));
|
mutations.push(AssignId {
|
||||||
// }
|
id: new_id,
|
||||||
|
path: &template.template.node_paths[idx][1..],
|
||||||
|
});
|
||||||
|
|
||||||
// // In the generated edits, we want to pick off from where we left off.
|
0
|
||||||
// boundary_mut.mutations.borrow_mut().edits.extend(split_off);
|
|
||||||
|
|
||||||
// boundary_mut
|
|
||||||
// .waiting_on
|
|
||||||
// .borrow_mut()
|
|
||||||
// .extend(self.collected_leaves.drain(..));
|
|
||||||
|
|
||||||
// 0
|
|
||||||
|
|
||||||
// let boudary = self.scopes[scope.0]
|
|
||||||
// .consume_context::<SuspenseContext>()
|
|
||||||
// .unwrap();
|
|
||||||
|
|
||||||
// boudary
|
|
||||||
// .waiting_on
|
|
||||||
// .borrow_mut()
|
|
||||||
// .extend(self.collected_leaves.drain(..));
|
|
||||||
|
|
||||||
// if boudary.placeholder.get().is_none() {
|
|
||||||
// boudary.placeholder.set(Some(new_id));
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take the rendered nodes from a component and handle them if they were async
|
/// Take the rendered nodes from a component and handle them if they were async
|
||||||
///
|
///
|
||||||
/// IE simply assign an ID to the placeholder
|
/// IE simply assign an ID to the placeholder
|
||||||
fn create_component_placeholder(
|
fn mount_component_placeholder(
|
||||||
&mut self,
|
&mut self,
|
||||||
template: &VNode,
|
template: &VNode,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
component: &VComponent,
|
|
||||||
scope: ScopeId,
|
scope: ScopeId,
|
||||||
mutations: &mut Mutations,
|
mutations: &mut Mutations,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let new_id = self.next_element(template, template.template.node_paths[idx]);
|
let new_id = self.next_element(template, template.template.node_paths[idx]);
|
||||||
|
|
||||||
// Set the placeholder of the component
|
|
||||||
component.placeholder.set(Some(new_id));
|
|
||||||
|
|
||||||
// Set the placeholder of the scope
|
// Set the placeholder of the scope
|
||||||
self.scopes[scope.0].placeholder.set(Some(new_id));
|
self.scopes[scope.0].placeholder.set(Some(new_id));
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ impl ScopeState {
|
|||||||
render_fn: component as *const (),
|
render_fn: component as *const (),
|
||||||
static_props: P::IS_STATIC,
|
static_props: P::IS_STATIC,
|
||||||
props: Cell::new(Some(extended)),
|
props: Cell::new(Some(extended)),
|
||||||
placeholder: Cell::new(None),
|
|
||||||
scope: Cell::new(None),
|
scope: Cell::new(None),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,16 @@ pub struct VNode<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VNode<'a> {
|
impl<'a> VNode<'a> {
|
||||||
pub fn single_component(
|
pub fn placeholder_template(cx: &'a ScopeState) -> Self {
|
||||||
|
Self::template_from_dynamic_node(
|
||||||
|
cx,
|
||||||
|
DynamicNode::Placeholder(Cell::new(ElementId(0))),
|
||||||
|
"dioxus-placeholder",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn template_from_dynamic_node(
|
||||||
cx: &'a ScopeState,
|
cx: &'a ScopeState,
|
||||||
node: DynamicNode<'a>,
|
node: DynamicNode<'a>,
|
||||||
id: &'static str,
|
id: &'static str,
|
||||||
@@ -134,7 +143,6 @@ impl<'a> DynamicNode<'a> {
|
|||||||
pub struct VComponent<'a> {
|
pub struct VComponent<'a> {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub static_props: bool,
|
pub static_props: bool,
|
||||||
pub placeholder: Cell<Option<ElementId>>,
|
|
||||||
pub scope: Cell<Option<ScopeId>>,
|
pub scope: Cell<Option<ScopeId>>,
|
||||||
pub props: Cell<Option<Box<dyn AnyProps<'a> + 'a>>>,
|
pub props: Cell<Option<Box<dyn AnyProps<'a> + 'a>>>,
|
||||||
pub render_fn: *const (),
|
pub render_fn: *const (),
|
||||||
@@ -145,7 +153,6 @@ impl<'a> std::fmt::Debug for VComponent<'a> {
|
|||||||
f.debug_struct("VComponent")
|
f.debug_struct("VComponent")
|
||||||
.field("name", &self.name)
|
.field("name", &self.name)
|
||||||
.field("static_props", &self.static_props)
|
.field("static_props", &self.static_props)
|
||||||
.field("placeholder", &self.placeholder)
|
|
||||||
.field("scope", &self.scope)
|
.field("scope", &self.scope)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ pub struct SuspenseBoundary {
|
|||||||
pub mutations: RefCell<Mutations<'static>>,
|
pub mutations: RefCell<Mutations<'static>>,
|
||||||
pub placeholder: Cell<Option<ElementId>>,
|
pub placeholder: Cell<Option<ElementId>>,
|
||||||
|
|
||||||
|
pub created_on_stack: Cell<usize>,
|
||||||
|
|
||||||
// whenever the suspense resolves, we call this onresolve function
|
// whenever the suspense resolves, we call this onresolve function
|
||||||
// this lets us do things like putting up a loading spinner
|
// this lets us do things like putting up a loading spinner
|
||||||
//
|
//
|
||||||
@@ -41,6 +43,7 @@ impl SuspenseBoundary {
|
|||||||
waiting_on: Default::default(),
|
waiting_on: Default::default(),
|
||||||
mutations: RefCell::new(Mutations::new(0)),
|
mutations: RefCell::new(Mutations::new(0)),
|
||||||
placeholder: Cell::new(None),
|
placeholder: Cell::new(None),
|
||||||
|
created_on_stack: Cell::new(0),
|
||||||
onresolve: None,
|
onresolve: None,
|
||||||
onstart: None,
|
onstart: None,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use futures_util::{pin_mut, StreamExt};
|
|||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
|
borrow::BorrowMut,
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
collections::{BTreeSet, HashMap},
|
collections::{BTreeSet, HashMap},
|
||||||
future::Future,
|
future::Future,
|
||||||
@@ -541,9 +542,35 @@ impl VirtualDom {
|
|||||||
self.dirty_scopes.remove(&dirty);
|
self.dirty_scopes.remove(&dirty);
|
||||||
|
|
||||||
// if the scope is currently suspended, then we should skip it, ignoring any tasks calling for an update
|
// if the scope is currently suspended, then we should skip it, ignoring any tasks calling for an update
|
||||||
if !self.is_scope_suspended(dirty.id) {
|
if self.is_scope_suspended(dirty.id) {
|
||||||
self.run_scope(dirty.id);
|
continue;
|
||||||
self.diff_scope(&mut mutations, dirty.id);
|
}
|
||||||
|
|
||||||
|
// Save the current mutations length so we can split them into boundary
|
||||||
|
let mutations_to_this_point = mutations.len();
|
||||||
|
|
||||||
|
self.diff_scope(&mut mutations, dirty.id);
|
||||||
|
|
||||||
|
// If suspended leaves are present, then we should find the boundary for this scope and attach things
|
||||||
|
// No placeholder necessary since this is a diff
|
||||||
|
if !self.collected_leaves.is_empty() {
|
||||||
|
let mut boundary = self.scopes[dirty.id.0]
|
||||||
|
.consume_context::<SuspenseContext>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let boundary_mut = boundary.borrow_mut();
|
||||||
|
|
||||||
|
// Attach mutations
|
||||||
|
boundary_mut
|
||||||
|
.mutations
|
||||||
|
.borrow_mut()
|
||||||
|
.extend(mutations.split_off(mutations_to_this_point));
|
||||||
|
|
||||||
|
// Attach suspended leaves
|
||||||
|
boundary
|
||||||
|
.waiting_on
|
||||||
|
.borrow_mut()
|
||||||
|
.extend(self.collected_leaves.drain(..));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ async fn it_works() {
|
|||||||
fn app(cx: Scope) -> Element {
|
fn app(cx: Scope) -> Element {
|
||||||
println!("running root app");
|
println!("running root app");
|
||||||
|
|
||||||
VNode::single_component(
|
VNode::template_from_dynamic_node(
|
||||||
cx,
|
cx,
|
||||||
cx.component(suspense_boundary, (), "suspense_boundary"),
|
cx.component(suspense_boundary, (), "suspense_boundary"),
|
||||||
"app",
|
"app",
|
||||||
@@ -29,7 +29,7 @@ fn suspense_boundary(cx: Scope) -> Element {
|
|||||||
cx.provide_context(Rc::new(RefCell::new(SuspenseBoundary::new(cx.scope_id()))))
|
cx.provide_context(Rc::new(RefCell::new(SuspenseBoundary::new(cx.scope_id()))))
|
||||||
});
|
});
|
||||||
|
|
||||||
VNode::single_component(cx, cx.component(async_child, (), "async_child"), "app")
|
VNode::template_from_dynamic_node(cx, cx.component(async_child, (), "async_child"), "app")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn async_child(cx: Scope<'_>) -> Element {
|
async fn async_child(cx: Scope<'_>) -> Element {
|
||||||
@@ -47,7 +47,7 @@ async fn async_child(cx: Scope<'_>) -> Element {
|
|||||||
|
|
||||||
println!("Future awaited and complete");
|
println!("Future awaited and complete");
|
||||||
|
|
||||||
VNode::single_component(cx, cx.component(async_text, (), "async_text"), "app")
|
VNode::template_from_dynamic_node(cx, cx.component(async_text, (), "async_text"), "app")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn async_text(cx: Scope<'_>) -> Element {
|
async fn async_text(cx: Scope<'_>) -> Element {
|
||||||
|
|||||||
@@ -323,6 +323,8 @@ export class Interpreter {
|
|||||||
this.RemoveEventListener(edit.id, edit.event_name);
|
this.RemoveEventListener(edit.id, edit.event_name);
|
||||||
break;
|
break;
|
||||||
case "NewEventListener":
|
case "NewEventListener":
|
||||||
|
// console.log("creating listener! ", edit);
|
||||||
|
|
||||||
// this handler is only provided on desktop implementations since this
|
// this handler is only provided on desktop implementations since this
|
||||||
// method is not used by the web implementation
|
// method is not used by the web implementation
|
||||||
let handler = (event) => {
|
let handler = (event) => {
|
||||||
|
|||||||
Reference in new issue
Block a user