Fix context in outlets (#374)

* Add `Scope::parent()` to make access to parent `Scope` possible.

* Handle context properly in nested routes
This commit is contained in:
Greg Johnston 2023-01-25 22:02:43 -05:00 committed by GitHub
parent 154e42f3f4
commit fc7199f188
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 13 deletions

View file

@ -234,6 +234,19 @@ impl Scope {
f(&mut scope.borrow_mut());
})
}
/// Returns the the parent Scope, if any.
pub fn parent(&self) -> Option<Scope> {
with_runtime(self.runtime, |runtime| {
runtime.scope_parents.borrow().get(self.id).copied()
})
.ok()
.flatten()
.map(|id| Scope {
runtime: self.runtime,
id,
})
}
}
/// Creates a cleanup function, which will be run when a [Scope] is disposed.

View file

@ -11,7 +11,7 @@ pub fn Outlet(cx: Scope) -> impl IntoView {
let is_showing = Rc::new(Cell::new(None::<(usize, Scope)>));
let (outlet, set_outlet) = create_signal(cx, None::<View>);
create_isomorphic_effect(cx, move |_| {
match (route.child(), &is_showing.get()) {
match (route.child(cx), &is_showing.get()) {
(None, prev) => {
if let Some(prev_scope) = prev.map(|(_, scope)| scope) {
prev_scope.dispose();

View file

@ -79,7 +79,7 @@ impl RouteContext {
pub(crate) fn new(
cx: Scope,
router: &RouterContext,
child: impl Fn() -> Option<RouteContext> + 'static,
child: impl Fn(Scope) -> Option<RouteContext> + 'static,
matcher: impl Fn() -> Option<RouteMatch> + 'static,
) -> Option<Self> {
let base = router.base();
@ -151,7 +151,7 @@ impl RouteContext {
cx,
id: 0,
base_path: path.to_string(),
child: Box::new(|| None),
child: Box::new(|_| None),
path: RefCell::new(path.to_string()),
original_path: path.to_string(),
params: create_memo(cx, |_| ParamsMap::new()),
@ -166,8 +166,8 @@ impl RouteContext {
}
/// The nested child route, if any.
pub fn child(&self) -> Option<RouteContext> {
(self.inner.child)()
pub fn child(&self, cx: Scope) -> Option<RouteContext> {
(self.inner.child)(cx)
}
/// The view associated with the current route.
@ -180,7 +180,7 @@ pub(crate) struct RouteContextInner {
cx: Scope,
base_path: String,
pub(crate) id: usize,
pub(crate) child: Box<dyn Fn() -> Option<RouteContext>>,
pub(crate) child: Box<dyn Fn(Scope) -> Option<RouteContext>>,
pub(crate) path: RefCell<String>,
pub(crate) original_path: String,
pub(crate) params: Memo<ParamsMap>,
@ -202,7 +202,6 @@ impl std::fmt::Debug for RouteContextInner {
f.debug_struct("RouteContextInner")
.field("path", &self.path)
.field("ParamsMap", &self.params)
.field("child", &(self.child)())
.finish()
}
}

View file

@ -24,10 +24,8 @@ pub fn Routes(
#[prop(optional)] base: Option<String>,
children: Box<dyn FnOnce(Scope) -> Fragment>,
) -> impl IntoView {
let router = use_context::<RouterContext>(cx).unwrap_or_else(|| {
log::warn!("<Routes/> component should be nested within a <Router/>.");
panic!()
});
let router = use_context::<RouterContext>(cx)
.expect("<Routes/> component should be nested within a <Router/>.");
let base_route = router.base();
let mut branches = Vec::new();
@ -86,6 +84,8 @@ pub fn Routes(
.map(|prev_matches| next_matches.len() == prev_matches.len())
.unwrap_or(false);
let prev_cx = Rc::new(Cell::new(cx));
for i in 0..next_matches.len() {
let next = next.clone();
let prev_match = prev_matches.and_then(|p| p.get(i));
@ -112,17 +112,19 @@ pub fn Routes(
root_equal.set(false);
}
let disposer = cx.child_scope({
let disposer = prev_cx.get().child_scope({
let next = next.clone();
let router = Rc::clone(&router.inner);
let prev_cx = Rc::clone(&prev_cx);
move |cx| {
prev_cx.set(cx);
let next = next.clone();
let next_ctx = RouteContext::new(
cx,
&RouterContext { inner: router },
{
let next = next.clone();
move || {
move |cx| {
if let Some(route_states) =
use_context::<Memo<RouterState>>(cx)
{