progress on error boundary that works with nested reactivity

This commit is contained in:
Greg Johnston 2024-02-19 21:12:10 -05:00
parent a7162d7907
commit 6c2469ec3a
3 changed files with 123 additions and 57 deletions

View File

@ -2,7 +2,7 @@ use leptos::{component, create_signal, prelude::*, view, IntoView};
#[component]
pub fn App() -> impl IntoView {
let (value, set_value) = create_signal(Ok(0)); //"foo".parse::<i32>());
let (value, set_value) = create_signal(Ok(0));//"".parse::<i32>());
let guard = value.read();
view! {

View File

@ -70,42 +70,41 @@ where
}
fn try_build(mut self) -> Result<Self::FallibleState, Self::Error> {
let initial = untrack(|| self().try_build())?;
let parent = Observer::get();
let effect = RenderEffect::new_with_value(
{
move |prev| {
let value = self();
if let Some(mut state) = prev {
match state {
Ok(ref mut state) => {
if let Err(e) = value.try_rebuild(state) {
if let Some(parent) = &parent {
crate::log(
"telling parent to check itself",
);
parent.mark_check();
}
return Err(Some(e));
let effect = RenderEffect::new({
move |prev| {
let value = self();
if let Some(mut state) = prev {
match state {
Ok(ref mut state) => {
if let Err(e) = value.try_rebuild(state) {
if let Some(parent) = &parent {
parent.mark_check();
}
}
Err(e) => {
//if let Some(parent) = parent {
crate::log("need to tell parent to rerender");
//}
return Err(e);
return Err(Some(e));
}
}
state
} else {
unreachable!()
Err(_) => {
if let Some(parent) = &parent {
parent.mark_check();
}
crate::log("HERE");
return value.try_build().map_err(Some);
}
}
state
} else {
value.try_build().map_err(Some)
}
},
Some(Ok(initial)),
);
Ok(effect.into())
}
});
effect
.with_value_mut(|inner| match inner {
Err(e) if e.is_some() => Err(e.take().unwrap()),
_ => Ok(()),
})
.expect("RenderEffect should run once synchronously")
.map(|_| effect.into())
}
#[track_caller]

View File

@ -141,35 +141,70 @@ where
type Error = NeverError;
fn build(mut self) -> Self::State {
let state = match self.child.try_build() {
Ok(inner) => Either::Left(inner),
Err(e) => Either::Right((self.fal)(e).build()),
let inner = match self.child.try_build() {
Ok(inner) => TryStateState::Success(Some(inner)),
Err(e) => TryStateState::InitialFail((self.fal)(e).build()),
};
let marker = Rndr::create_placeholder();
TryState { state, marker }
TryState { inner, marker }
}
fn rebuild(mut self, state: &mut Self::State) {
let marker = state.marker.as_ref();
match &mut state.state {
Either::Left(ref mut old) => {
if let Err(e) = self.child.try_rebuild(old) {
old.unmount();
let res = match &mut state.inner {
TryStateState::Success(old) => {
let old_unwrapped =
old.as_mut().expect("children removed before expected");
crate::log("rebuilding successful version");
if let Err(e) = self.child.try_rebuild(old_unwrapped) {
old_unwrapped.unmount();
drop(old_unwrapped);
let mut new_state = (self.fal)(e).build();
Rndr::mount_before(&mut new_state, marker);
state.state = Either::Right(new_state);
Some(Err((old.take(), new_state)))
} else {
None
}
}
Either::Right(old) => match self.child.try_build() {
TryStateState::InitialFail(old) => match self.child.try_build() {
Err(e) => {
(self.fal)(e).rebuild(old);
None
}
Ok(mut new_state) => {
old.unmount();
Rndr::mount_before(&mut new_state, marker);
state.state = Either::Left(new_state);
}
Err(e) => {
(self.fal)(e).rebuild(old);
Some(Ok(new_state))
}
},
TryStateState::SubsequentFail {
ref mut children,
fallback,
} => match self.child.try_rebuild(
children.as_mut().expect("children removed before expected"),
) {
Err(e) => {
(self.fal)(e).rebuild(fallback);
None
}
Ok(()) => {
fallback.unmount();
Rndr::mount_before(children, marker);
Some(Ok(children
.take()
.expect("children removed before expected")))
}
},
};
match res {
Some(Ok(new_children)) => {
state.inner = TryStateState::Success(Some(new_children))
}
Some(Err((children, fallback))) => {
state.inner =
TryStateState::SubsequentFail { children, fallback }
}
None => {}
}
}
@ -230,10 +265,24 @@ where
Fal: Render<Rndr>,
Rndr: Renderer,
{
state: Either<T::FallibleState, Fal::State>,
inner: TryStateState<T, Fal, Rndr>,
marker: Rndr::Placeholder,
}
enum TryStateState<T, Fal, Rndr>
where
T: Render<Rndr>,
Fal: Render<Rndr>,
Rndr: Renderer,
{
Success(Option<T::FallibleState>),
InitialFail(Fal::State),
SubsequentFail {
children: Option<T::FallibleState>,
fallback: Fal::State,
},
}
impl<T, Fal, Rndr> Mountable<Rndr> for TryState<T, Fal, Rndr>
where
T: Render<Rndr>,
@ -241,9 +290,15 @@ where
Rndr: Renderer,
{
fn unmount(&mut self) {
match &mut self.state {
Either::Left(left) => left.unmount(),
Either::Right(right) => right.unmount(),
match &mut self.inner {
TryStateState::Success(m) => m
.as_mut()
.expect("children removed before expected")
.unmount(),
TryStateState::InitialFail(m) => m.unmount(),
TryStateState::SubsequentFail { fallback, .. } => {
fallback.unmount()
}
}
self.marker.unmount();
}
@ -254,12 +309,16 @@ where
marker: Option<&<Rndr as Renderer>::Node>,
) {
self.marker.mount(parent, marker);
match &mut self.state {
Either::Left(left) => {
left.mount(parent, Some(self.marker.as_ref()))
match &mut self.inner {
TryStateState::Success(m) => m
.as_mut()
.expect("children removed before expected")
.mount(parent, Some(self.marker.as_ref())),
TryStateState::InitialFail(m) => {
m.mount(parent, Some(self.marker.as_ref()))
}
Either::Right(right) => {
right.mount(parent, Some(self.marker.as_ref()))
TryStateState::SubsequentFail { fallback, .. } => {
fallback.mount(parent, Some(self.marker.as_ref()))
}
}
}
@ -269,9 +328,17 @@ where
parent: &<Rndr as Renderer>::Element,
child: &mut dyn Mountable<Rndr>,
) -> bool {
match &self.state {
Either::Left(left) => left.insert_before_this(parent, child),
Either::Right(right) => right.insert_before_this(parent, child),
match &self.inner {
TryStateState::Success(m) => m
.as_ref()
.expect("children removed before expected")
.insert_before_this(parent, child),
TryStateState::InitialFail(m) => {
m.insert_before_this(parent, child)
}
TryStateState::SubsequentFail { fallback, .. } => {
fallback.insert_before_this(parent, child)
}
}
}
}