wip: memoize dom in the prescence of identical components

This commit is contained in:
Jonathan Kelley committed 2022-01-05 00:27:22 -05:00
1 parent 7cf15ee4e8
commit cb2782b4bb
27 files changed
+474 -219

No files matched your search

@@ -1,4 +1,4 @@
use crate::{Routable, RouterService};
use crate::RouterService;
use dioxus::Attribute;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
@@ -6,8 +6,8 @@ use dioxus_core_macro::{format_args_f, rsx, Props};
use dioxus_html as dioxus_elements;
#[derive(Props)]
pub struct LinkProps<'a, R: Routable> {
to: R,
pub struct LinkProps<'a> {
to: &'a str,
/// The url that gets pushed to the history stack
///
@@ -28,6 +28,9 @@ pub struct LinkProps<'a, R: Routable> {
#[props(default, setter(strip_option))]
class: Option<&'a str>,
#[props(default, setter(strip_option))]
id: Option<&'a str>,
children: Element<'a>,
#[props(default)]
@@ -35,13 +38,21 @@ pub struct LinkProps<'a, R: Routable> {
}
#[allow(non_snake_case)]
pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element {
let service = cx.consume_context::<RouterService<R>>()?;
pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
log::debug!("rendering link {:?}", cx.scope_id());
let service = cx.consume_context::<RouterService>()?;
cx.render(rsx! {
a {
href: "#",
href: "{cx.props.to}",
class: format_args!("{}", cx.props.class.unwrap_or("")),
onclick: move |_| service.push_route(cx.props.to.clone()),
id: format_args!("{}", cx.props.id.unwrap_or("")),
prevent_default: "onclick",
onclick: move |_| {
log::debug!("clicked");
service.push_route(cx.props.to.clone());
},
&cx.props.children
}
})
+58
View File
@@ -0,0 +1,58 @@
use dioxus_core::Element;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_core_macro::Props;
use dioxus_core_macro::*;
use dioxus_html as dioxus_elements;
use crate::{RouteContext, RouterService};
#[derive(Props)]
pub struct RouteProps<'a> {
to: &'a str,
children: Element<'a>,
}
pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element {
// now we want to submit
let router_root = cx
.use_hook(|_| cx.consume_context::<RouterService>())
.as_ref()?;
cx.use_hook(|_| {
// create a bigger, better, longer route if one above us exists
let total_route = match cx.consume_context::<RouteContext>() {
Some(ctx) => format!("{}", ctx.total_route.clone()),
None => format!("{}", cx.props.to.clone()),
};
// provide our route context
let route_context = cx.provide_context(RouteContext {
declared_route: cx.props.to.to_string(),
total_route,
});
// submit our rout
router_root.register_total_route(route_context.total_route.clone(), cx.scope_id());
Some(RouteInner {})
});
log::debug!("rendering route {:?}", cx.scope_id());
if router_root.should_render(cx.scope_id()) {
log::debug!("route {:?} produced nodes", cx.scope_id());
cx.render(rsx!(&cx.props.children))
} else {
None
}
}
struct RouteInner {}
impl Drop for RouteInner {
fn drop(&mut self) {
// todo!()
}
}
+29
View File
@@ -0,0 +1,29 @@
use dioxus_core::Element;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_core_macro::*;
use dioxus_html as dioxus_elements;
use crate::RouterService;
#[derive(Props)]
pub struct RouterProps<'a> {
children: Element<'a>,
#[props(default, setter(strip_option))]
onchange: Option<&'a Fn(&'a str)>,
}
pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element {
let p = cx.use_hook(|_| {
let update = cx.schedule_update_any();
cx.provide_context(RouterService::new(update, cx.scope_id()))
});
log::debug!("rendering router {:?}", cx.scope_id());
cx.render(rsx!(
div { &cx.props.children }
))
}
+30
View File
@@ -0,0 +1,30 @@
use dioxus_core::ScopeState;
pub struct UseRoute<'a> {
cur_route: String,
cx: &'a ScopeState,
}
impl<'a> UseRoute<'a> {
/// Parse the query part of the URL
pub fn param<T>(&self, param: &str) -> Option<&T> {
todo!()
}
pub fn nth_segment(&self, n: usize) -> Option<&str> {
todo!()
}
pub fn last_segment(&self) -> Option<&str> {
todo!()
}
/// Parse the segments of the URL, using named parameters (defined in your router)
pub fn segment<T>(&self, name: &str) -> Option<&T> {
todo!()
}
}
pub fn use_route(cx: &ScopeState) -> UseRoute {
todo!()
}
+20 -25
View File
@@ -7,17 +7,6 @@
//!
//! ```rust
//! fn app(cx: Scope) -> Element {
//! let route = use_router(&cx, |svc, path| {
//! match path {
//! "/about" => Route::About,
//! _ => Route::Home,
//! }
//! });
//!
//! match route {
//! Route::Home => rsx!(cx, h1 { "Home" }),
//! Route::About => rsx!(cx, h1 { "About" }),
//! }
//! }
//!
//!
@@ -35,22 +24,28 @@
//!
//!
mod link;
mod hooks {
mod use_route;
pub use use_route::*;
}
pub use hooks::*;
mod components {
mod router;
pub use router::*;
mod route;
pub use route::*;
mod link;
pub use link::*;
}
pub use components::*;
mod platform;
mod routecontext;
mod service;
mod userouter;
mod utils;
pub use link::*;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
pub use routecontext::*;
pub use service::*;
pub use userouter::*;
pub trait Routable:
'static + Send + Clone + PartialEq + Serialize + DeserializeOwned + Default
{
}
impl<T> Routable for T where
T: 'static + Send + Clone + PartialEq + Serialize + DeserializeOwned + Default
{
}
-7
View File
@@ -1,7 +0,0 @@
pub fn parse_current_route() -> String {
todo!()
}
pub(crate) fn get_base_route() -> String {
todo!()
}
+11
View File
@@ -0,0 +1,11 @@
use url::Url;
pub trait RouterProvider {
fn get_current_route(&self) -> String;
fn subscribe_to_route_changes(&self, callback: Box<dyn Fn(String)>);
}
enum RouteChange {
LinkTo(String),
Back(String),
}
+7
View File
@@ -0,0 +1,7 @@
pub struct RouteContext {
// "/name/:id"
pub declared_route: String,
// "app/name/:id"
pub total_route: String,
}
+68 -11
View File
@@ -1,18 +1,75 @@
use crate::Routable;
use std::{cell::RefCell, rc::Rc};
use gloo::history::{BrowserHistory, History, HistoryListener};
use std::{cell::RefCell, collections::HashMap, rc::Rc};
pub struct RouterService<R: Routable> {
pub(crate) regen_route: Rc<dyn Fn()>,
pub(crate) pending_routes: RefCell<Vec<R>>,
use dioxus_core::ScopeId;
pub struct RouterService {
pub(crate) regen_route: Rc<dyn Fn(ScopeId)>,
history: RefCell<BrowserHistory>,
registerd_routes: RefCell<RouteSlot>,
slots: RefCell<HashMap<ScopeId, String>>,
cur_root: RefCell<String>,
listener: HistoryListener,
}
impl<R: Routable> RouterService<R> {
pub fn current_path(&self) -> &str {
todo!()
enum RouteSlot {
Routes {
// the partial route
partial: String,
// the total route
total: String,
// Connections to other routs
rest: Vec<RouteSlot>,
},
}
impl RouterService {
pub fn new(regen_route: Rc<dyn Fn(ScopeId)>, root_scope: ScopeId) -> Self {
let history = BrowserHistory::default();
let location = history.location();
let path = location.path();
let regen = Rc::clone(&regen_route);
let listener = history.listen(move || {
regen(root_scope);
});
Self {
registerd_routes: RefCell::new(RouteSlot::Routes {
partial: String::from("/"),
total: String::from("/"),
rest: Vec::new(),
}),
history: RefCell::new(history),
regen_route,
slots: Default::default(),
cur_root: RefCell::new(path.to_string()),
listener,
}
}
pub fn push_route(&self, route: R) {
self.pending_routes.borrow_mut().push(route);
(self.regen_route)();
pub fn push_route(&self, route: &str) {
self.history.borrow_mut().push(route);
}
pub fn register_total_route(&self, route: String, scope: ScopeId) {
self.slots.borrow_mut().insert(scope, route);
}
pub fn should_render(&self, scope: ScopeId) -> bool {
let location = self.history.borrow().location();
let path = location.path();
let roots = self.slots.borrow();
let root = roots.get(&scope);
match root {
Some(r) => r == path,
None => false,
}
}
}
-33
View File
@@ -1,33 +0,0 @@
use std::{cell::RefCell, rc::Rc};
use crate::{Routable, RouterCfg, RouterService};
use dioxus_core::ScopeState;
/// Initialize the app's router service and provide access to `Link` components
pub fn use_router<'a, R: Routable>(cx: &'a ScopeState, _cfg: impl FnOnce(&mut RouterCfg)) -> &'a R {
let router = cx.use_hook(|_| {
let svc: RouterService<R> = RouterService {
regen_route: cx.schedule_update(),
pending_routes: RefCell::new(Vec::new()),
};
let first_path = R::default();
cx.provide_context(svc);
UseRouterInner {
svc: cx.consume_context::<RouterService<R>>().unwrap(),
history: vec![first_path],
}
});
let mut pending_routes = router.svc.pending_routes.borrow_mut();
for route in pending_routes.drain(..) {
router.history.push(route);
}
router.history.last().unwrap()
}
struct UseRouterInner<R: Routable> {
svc: Rc<RouterService<R>>,
history: Vec<R>,
}