chore: finish tweaking context api
This commit is contained in:
1 parent
99a818996e
commit
df4a1b6c7a
12 files changed
+76
-81
No files matched your search
@@ -1,4 +1,5 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::innerlude::{VComponent, VText};
|
||||
use crate::mutations::Mutation;
|
||||
@@ -340,7 +341,7 @@ impl<'b> VirtualDom {
|
||||
}
|
||||
|
||||
// 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>() {
|
||||
let boundary = match self.scopes[scope.0].has_context::<Rc<SuspenseContext>>() {
|
||||
Some(boundary) => boundary,
|
||||
_ => return created,
|
||||
};
|
||||
|
||||
@@ -34,9 +34,9 @@ impl VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn acquire_suspense_boundary<'a>(&self, id: ScopeId) -> Rc<SuspenseContext> {
|
||||
pub(crate) fn acquire_suspense_boundary<'a>(&'a self, id: ScopeId) -> Rc<SuspenseContext> {
|
||||
self.scopes[id.0]
|
||||
.consume_context::<SuspenseContext>()
|
||||
.consume_context::<Rc<SuspenseContext>>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
|
||||
+16
-16
@@ -81,7 +81,7 @@ pub struct ScopeState {
|
||||
pub(crate) hook_list: RefCell<Vec<*mut dyn Any>>,
|
||||
pub(crate) hook_idx: Cell<usize>,
|
||||
|
||||
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
||||
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Box<dyn Any>>>,
|
||||
|
||||
pub(crate) tasks: Rc<Scheduler>,
|
||||
pub(crate) spawned_tasks: HashSet<TaskId>,
|
||||
@@ -250,17 +250,18 @@ impl<'src> ScopeState {
|
||||
}
|
||||
|
||||
/// Return any context of type T if it exists on this scope
|
||||
pub fn has_context<T: 'static>(&self) -> Option<Rc<T>> {
|
||||
pub fn has_context<T: 'static + Clone>(&self) -> Option<T> {
|
||||
self.shared_contexts
|
||||
.borrow()
|
||||
.get(&TypeId::of::<T>())?
|
||||
.clone()
|
||||
.downcast()
|
||||
.ok()
|
||||
.downcast_ref::<T>()
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Try to retrieve a shared state with type `T` from any parent scope.
|
||||
pub fn consume_context<T: 'static>(&self) -> Option<Rc<T>> {
|
||||
///
|
||||
/// Clones the state if it exists.
|
||||
pub fn consume_context<T: 'static + Clone>(&self) -> Option<T> {
|
||||
if let Some(this_ctx) = self.has_context() {
|
||||
return Some(this_ctx);
|
||||
}
|
||||
@@ -270,7 +271,7 @@ impl<'src> ScopeState {
|
||||
// safety: all parent pointers are valid thanks to the bump arena
|
||||
let parent = unsafe { &*parent_ptr };
|
||||
if let Some(shared) = parent.shared_contexts.borrow().get(&TypeId::of::<T>()) {
|
||||
return shared.clone().downcast().ok();
|
||||
return shared.downcast_ref::<T>().cloned();
|
||||
}
|
||||
search_parent = parent.parent;
|
||||
}
|
||||
@@ -303,15 +304,14 @@ impl<'src> ScopeState {
|
||||
/// render!(div { "hello {state.0}" })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn provide_context<T: 'static>(&self, value: T) -> Rc<T> {
|
||||
let mut contexts = self.shared_contexts.borrow_mut();
|
||||
contexts.insert(TypeId::of::<T>(), Rc::new(value));
|
||||
contexts
|
||||
.get(&TypeId::of::<T>())
|
||||
.unwrap()
|
||||
.clone()
|
||||
.downcast()
|
||||
.unwrap()
|
||||
pub fn provide_context<T: 'static + Clone>(&self, value: T) -> T {
|
||||
let value2 = value.clone();
|
||||
|
||||
self.shared_contexts
|
||||
.borrow_mut()
|
||||
.insert(TypeId::of::<T>(), Box::new(value));
|
||||
|
||||
value2
|
||||
}
|
||||
|
||||
/// Pushes the future onto the poll queue to be polled after the component renders.
|
||||
|
||||
@@ -249,10 +249,10 @@ impl VirtualDom {
|
||||
// This could be unexpected, so we might rethink this behavior later
|
||||
//
|
||||
// We *could* just panic if the suspense boundary is not found
|
||||
root.provide_context(SuspenseContext::new(ScopeId(0)));
|
||||
root.provide_context(Rc::new(SuspenseContext::new(ScopeId(0))));
|
||||
|
||||
// Unlike react, we provide a default error boundary that just renders the error as a string
|
||||
root.provide_context(ErrorBoundary::new(ScopeId(0)));
|
||||
root.provide_context(Rc::new(ErrorBoundary::new(ScopeId(0))));
|
||||
|
||||
// the root element is always given element ID 0 since it's the container for the entire tree
|
||||
dom.elements.insert(ElementRef::null());
|
||||
@@ -296,7 +296,7 @@ impl VirtualDom {
|
||||
/// currently suspended.
|
||||
pub fn is_scope_suspended(&self, id: ScopeId) -> bool {
|
||||
!self.scopes[id.0]
|
||||
.consume_context::<SuspenseContext>()
|
||||
.consume_context::<Rc<SuspenseContext>>()
|
||||
.unwrap()
|
||||
.waiting_on
|
||||
.borrow()
|
||||
@@ -527,7 +527,7 @@ impl VirtualDom {
|
||||
// first, unload any complete suspense trees
|
||||
for finished_fiber in self.finished_fibers.drain(..) {
|
||||
let scope = &mut self.scopes[finished_fiber.0];
|
||||
let context = scope.has_context::<SuspenseContext>().unwrap();
|
||||
let context = scope.has_context::<Rc<SuspenseContext>>().unwrap();
|
||||
|
||||
self.mutations
|
||||
.templates
|
||||
@@ -565,7 +565,7 @@ impl VirtualDom {
|
||||
// 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>()
|
||||
.consume_context::<Rc<SuspenseContext>>()
|
||||
.unwrap();
|
||||
|
||||
let boundary_mut = boundary.borrow_mut();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::AtomRoot;
|
||||
use dioxus_core::ScopeState;
|
||||
use std::rc::Rc;
|
||||
|
||||
// Returns the atom root, initiaizing it at the root of the app if it does not exist.
|
||||
pub fn use_atom_root(cx: &ScopeState) -> &Rc<AtomRoot> {
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
use dioxus_core::ScopeState;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Consume some context in the tree
|
||||
pub fn use_context<T: 'static>(cx: &ScopeState) -> Option<&T> {
|
||||
cx.use_hook(|| cx.consume_context::<T>()).as_deref()
|
||||
/// Consume some context in the tree, providing a sharable handle to the value
|
||||
pub fn use_context<T: 'static + Clone>(cx: &ScopeState) -> Option<&T> {
|
||||
cx.use_hook(|| cx.consume_context::<T>()).as_ref()
|
||||
}
|
||||
|
||||
/// Provide some context via the tree and return a reference to it
|
||||
///
|
||||
/// Once the context has been provided, it is immutable. Mutations should be done via interior mutability.
|
||||
pub fn use_context_provider<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> &T {
|
||||
cx.use_hook(|| cx.provide_context(f()))
|
||||
pub fn use_context_provider<T: 'static + Clone>(cx: &ScopeState, f: impl FnOnce() -> T) -> &T {
|
||||
cx.use_hook(|| {
|
||||
let val = f();
|
||||
cx.provide_context(val.clone());
|
||||
val
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{use_route, RouterCore};
|
||||
use crate::{use_route, RouterContext};
|
||||
use dioxus::prelude::*;
|
||||
|
||||
/// Props for the [`Link`](struct.Link.html) component.
|
||||
@@ -77,7 +75,7 @@ pub struct LinkProps<'a> {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
|
||||
let svc = cx.use_hook(|| cx.consume_context::<Arc<RouterCore>>());
|
||||
let svc = use_context::<RouterContext>(cx);
|
||||
|
||||
let LinkProps {
|
||||
to,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use crate::{RouteContext, RouterContext};
|
||||
use dioxus::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{RouteContext, RouterCore};
|
||||
|
||||
/// Props for the [`Route`](struct.Route.html) component.
|
||||
#[derive(Props)]
|
||||
@@ -27,15 +25,13 @@ pub struct RouteProps<'a> {
|
||||
/// )
|
||||
/// ```
|
||||
pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element {
|
||||
let router_root = cx
|
||||
.use_hook(|| cx.consume_context::<Arc<RouterCore>>())
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let router_root = use_context::<RouterContext>(cx).unwrap();
|
||||
let root_context = use_context::<RouteContext>(cx);
|
||||
|
||||
cx.use_hook(|| {
|
||||
// create a bigger, better, longer route if one above us exists
|
||||
let total_route = match cx.consume_context::<RouteContext>() {
|
||||
Some(ctx) => ctx.total_route,
|
||||
let total_route = match root_context {
|
||||
Some(ctx) => ctx.total_route.clone(),
|
||||
None => cx.props.to.to_string(),
|
||||
};
|
||||
|
||||
@@ -46,7 +42,7 @@ pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element {
|
||||
});
|
||||
|
||||
// submit our rout
|
||||
router_root.register_total_route(route_context.total_route, cx.scope_id());
|
||||
router_root.register_total_route(route_context.total_route.clone(), cx.scope_id());
|
||||
});
|
||||
|
||||
log::trace!("Checking Route: {:?}", cx.props.to);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{cfg::RouterCfg, RouterCore};
|
||||
use crate::{cfg::RouterCfg, RouterContext, RouterService};
|
||||
use dioxus::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// The props for the [`Router`](fn.Router.html) component.
|
||||
#[derive(Props)]
|
||||
@@ -21,7 +20,7 @@ pub struct RouterProps<'a> {
|
||||
///
|
||||
/// This lets you easily implement redirects
|
||||
#[props(default)]
|
||||
pub onchange: EventHandler<'a, Arc<RouterCore>>,
|
||||
pub onchange: EventHandler<'a, RouterContext>,
|
||||
|
||||
/// Set the active class of all Link components contained in this router.
|
||||
///
|
||||
@@ -40,15 +39,15 @@ pub struct RouterProps<'a> {
|
||||
/// Will fallback to HashRouter is BrowserRouter is not available, or through configuration.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element {
|
||||
let svc = cx.use_hook(|| {
|
||||
cx.provide_context(RouterCore::new(
|
||||
let svc = use_context_provider(cx, || {
|
||||
RouterService::new(
|
||||
&cx,
|
||||
RouterCfg {
|
||||
base_url: cx.props.base_url.map(|s| s.to_string()),
|
||||
active_class: cx.props.active_class.map(|s| s.to_string()),
|
||||
initial_url: cx.props.initial_url.clone(),
|
||||
},
|
||||
))
|
||||
)
|
||||
});
|
||||
|
||||
// next time we run the rout_found will be filled
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{ParsedRoute, RouteContext, RouterCore, RouterService};
|
||||
use crate::{ParsedRoute, RouteContext, RouterContext};
|
||||
use dioxus::core::{ScopeId, ScopeState};
|
||||
use std::{borrow::Cow, str::FromStr, sync::Arc};
|
||||
use url::Url;
|
||||
@@ -7,11 +7,11 @@ use url::Url;
|
||||
/// context of a [`Router`]. If this function is called outside of a `Router`
|
||||
/// component it will panic.
|
||||
pub fn use_route(cx: &ScopeState) -> &UseRoute {
|
||||
let handle = cx.use_hook(|| {
|
||||
let router = cx
|
||||
.consume_context::<RouterService>()
|
||||
.expect("Cannot call use_route outside the scope of a Router component");
|
||||
let router = cx
|
||||
.consume_context::<RouterContext>()
|
||||
.expect("Cannot call use_route outside the scope of a Router component");
|
||||
|
||||
let handle = cx.use_hook(|| {
|
||||
let route_context = cx.consume_context::<RouteContext>();
|
||||
|
||||
router.subscribe_onchange(cx.scope_id());
|
||||
@@ -115,7 +115,7 @@ impl UseRoute {
|
||||
// and reveal our cached version of UseRoute to the component.
|
||||
struct UseRouteListener {
|
||||
state: UseRoute,
|
||||
router: Arc<RouterCore>,
|
||||
router: RouterContext,
|
||||
scope: ScopeId,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use crate::RouterService;
|
||||
use dioxus::core::ScopeState;
|
||||
use crate::RouterContext;
|
||||
use dioxus::{core::ScopeState, prelude::use_context};
|
||||
|
||||
/// This hook provides access to the `RouterService` for the app.
|
||||
pub fn use_router(cx: &ScopeState) -> &RouterService {
|
||||
cx.use_hook(|| {
|
||||
cx.consume_context::<RouterService>()
|
||||
.expect("Cannot call use_route outside the scope of a Router component")
|
||||
})
|
||||
pub fn use_router(cx: &ScopeState) -> &RouterContext {
|
||||
use_context::<RouterContext>(cx)
|
||||
.expect("Cannot call use_route outside the scope of a Router component")
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::cfg::RouterCfg;
|
||||
use dioxus::core::{ScopeId, ScopeState, VirtualDom};
|
||||
use std::any::Any;
|
||||
use std::sync::Weak;
|
||||
use std::rc::Weak;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::{HashMap, HashSet},
|
||||
@@ -14,6 +14,9 @@ use std::{
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
/// A clonable handle to the router
|
||||
pub type RouterContext = Rc<RouterService>;
|
||||
|
||||
/// An abstraction over the platform's history API.
|
||||
///
|
||||
/// The history is denoted using web-like semantics, with forward slashes delmitiing
|
||||
@@ -41,7 +44,7 @@ use url::Url;
|
||||
/// - On the web, this is a [`BrowserHistory`](https://docs.rs/gloo/0.3.0/gloo/history/struct.BrowserHistory.html).
|
||||
/// - On desktop, mobile, and SSR, this is just a Vec of Strings. Currently on
|
||||
/// desktop, there is no way to tap into forward/back for the app unless explicitly set.
|
||||
pub struct RouterCore {
|
||||
pub struct RouterService {
|
||||
pub(crate) route_found: Cell<Option<ScopeId>>,
|
||||
|
||||
pub(crate) stack: RefCell<Vec<Arc<ParsedRoute>>>,
|
||||
@@ -61,9 +64,6 @@ pub struct RouterCore {
|
||||
pub(crate) cfg: RouterCfg,
|
||||
}
|
||||
|
||||
/// A shared type for the RouterCore.
|
||||
pub type RouterService = Arc<RouterCore>;
|
||||
|
||||
/// A route is a combination of window title, saved state, and a URL.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParsedRoute {
|
||||
@@ -77,8 +77,8 @@ pub struct ParsedRoute {
|
||||
pub serialized_state: Option<String>,
|
||||
}
|
||||
|
||||
impl RouterCore {
|
||||
pub(crate) fn new(cx: &ScopeState, cfg: RouterCfg) -> Arc<Self> {
|
||||
impl RouterService {
|
||||
pub(crate) fn new(cx: &ScopeState, cfg: RouterCfg) -> RouterContext {
|
||||
#[cfg(feature = "web")]
|
||||
let history = Box::new(web::new());
|
||||
|
||||
@@ -99,7 +99,7 @@ impl RouterCore {
|
||||
None => Arc::new(history.init_location()),
|
||||
};
|
||||
|
||||
let svc = Arc::new(Self {
|
||||
let svc = Rc::new(Self {
|
||||
cfg,
|
||||
regen_any_route: cx.schedule_update_any(),
|
||||
router_id: cx.scope_id(),
|
||||
@@ -111,7 +111,7 @@ impl RouterCore {
|
||||
history,
|
||||
});
|
||||
|
||||
svc.history.attach_listeners(Arc::downgrade(&svc));
|
||||
svc.history.attach_listeners(Rc::downgrade(&svc));
|
||||
|
||||
svc
|
||||
}
|
||||
@@ -247,12 +247,9 @@ impl RouterCore {
|
||||
/// that owns the router.
|
||||
///
|
||||
/// This might change in the future.
|
||||
pub fn get_router_from_vdom(
|
||||
dom: &VirtualDom,
|
||||
target_scope: Option<ScopeId>,
|
||||
) -> Option<Arc<RouterCore>> {
|
||||
dom.get_scope(target_scope.unwrap_or(ScopeId(0)))
|
||||
.and_then(|scope| scope.consume_context::<Arc<RouterCore>>())
|
||||
pub fn get_router_from_vdom(dom: &VirtualDom, target_scope: ScopeId) -> Option<RouterContext> {
|
||||
dom.get_scope(target_scope)
|
||||
.and_then(|scope| scope.consume_context::<RouterContext>())
|
||||
}
|
||||
|
||||
fn clean_route(route: String) -> String {
|
||||
@@ -319,7 +316,7 @@ pub(crate) trait RouterProvider {
|
||||
fn replace(&self, route: &ParsedRoute);
|
||||
fn native_location(&self) -> Box<dyn Any>;
|
||||
fn init_location(&self) -> ParsedRoute;
|
||||
fn attach_listeners(&self, svc: Weak<RouterCore>);
|
||||
fn attach_listeners(&self, svc: Weak<RouterService>);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "web"))]
|
||||
@@ -350,7 +347,7 @@ mod hash {
|
||||
|
||||
fn replace(&self, _route: &ParsedRoute) {}
|
||||
|
||||
fn attach_listeners(&self, _svc: Weak<RouterCore>) {}
|
||||
fn attach_listeners(&self, _svc: Weak<RouterService>) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +415,7 @@ mod web {
|
||||
}
|
||||
}
|
||||
|
||||
fn attach_listeners(&self, svc: std::sync::Weak<crate::RouterCore>) {
|
||||
fn attach_listeners(&self, svc: std::sync::Weak<crate::RouterService>) {
|
||||
self._listener.set(Some(EventListener::new(
|
||||
&web_sys::window().unwrap(),
|
||||
"popstate",
|
||||
|
||||
Reference in new issue
Block a user