Feat: massive changes to definition of components

This change switches back to the original `ctx<props>` syntax for
commponents. This lets lifetime elision to remove the need to match
exactly which lifetime (props or ctx) gets  carried to the output. As
such, `Props` is currently required to be static. It *is* possible to
loosen this restriction, and will be done in the future, though only
through adding metadata about the props through the Props derive
macro. Implementing the IS_STATIC trait is unsafe, so the derive macro
will do it through some heuristics.

For now, this unlocks sharing vnodes from parents to children, enabling
pass-thru components, fragments, portals, etc.
This commit is contained in:
Jonathan Kelley committed 2021-06-01 18:33:15 -04:00
1 parent c1fd848f89
commit 508c560320
82 files changed
+692 -558

No files matched your search

+10 -10
View File
@@ -112,8 +112,8 @@ impl<T: Clone + PartialEq + 'static> Component for ContextProvider<T> {
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self {
children: props.children,
context: Rc::new(props.context),
children: ctx.children,
context: Rc::new(ctx.context),
consumers: RefCell::new(Vec::new()),
}
}
@@ -123,14 +123,14 @@ impl<T: Clone + PartialEq + 'static> Component for ContextProvider<T> {
}
fn change(&mut self, props: Self::Properties) -> bool {
let should_render = if self.children == props.children {
let should_render = if self.children == ctx.children {
false
} else {
self.children = props.children;
self.children = ctx.children;
true
};
let new_context = Rc::new(props.context);
let new_context = Rc::new(ctx.context);
if self.context != new_context {
self.context = new_context;
self.notify_consumers();
@@ -357,10 +357,10 @@ mod tests {
log::info!("Render counter {:?}", counter);
return html! {
<>
<div id=props.id.clone()>
<div id=ctx.id.clone()>
{ format!("total: {}", counter.borrow()) }
</div>
{ props.children.clone() }
{ ctx.children.clone() }
</>
};
}
@@ -384,14 +384,14 @@ mod tests {
let ctx = use_context::<Rc<MyContext>>().expect("context not passed down");
log::info!("============");
log::info!("ctx is {:#?}", ctx);
log::info!("magic is {:#?}", props.magic);
log::info!("magic is {:#?}", ctx.magic);
log::info!("outlet counter is {:#?}", ctx);
log::info!("============");
return html! {
<>
<div>{ format!("magic: {}\n", props.magic) }</div>
<div id=props.id.clone()>
<div>{ format!("magic: {}\n", ctx.magic) }</div>
<div id=ctx.id.clone()>
{ format!("current: {}, total: {}", ctx.0, counter.borrow()) }
</div>
</>
+3 -3
View File
@@ -277,8 +277,8 @@ mod tests {
type TProps = FunctionProps;
fn run(props: &Self::TProps) -> Html {
let effect_called = props.effect_called.clone();
let destroy_called = props.destroy_called.clone();
let effect_called = ctx.effect_called.clone();
let destroy_called = ctx.destroy_called.clone();
use_effect_with_deps(
move |_| {
effect_called();
@@ -297,7 +297,7 @@ mod tests {
if *show {
let effect_called: Rc<dyn Fn()> = Rc::new(move || set_show(false));
return html! {
<UseEffectComponent destroy_called=props.destroy_called.clone() effect_called=effect_called />
<UseEffectComponent destroy_called=ctx.destroy_called.clone() effect_called=effect_called />
};
} else {
return html! {
+1 -1
View File
@@ -48,7 +48,7 @@ use std::{cell::RefCell, rc::Rc};
pub fn use_ref<T: 'static>(initial_value: impl FnOnce() -> T + 'static) -> Rc<RefCell<T>> {
use_hook(
|| Rc::new(RefCell::new(initial_value())),
|state, _| state.clone(),
|state| state.clone(),
|_| {},
)
}
+1 -1
View File
@@ -40,7 +40,7 @@ uses the same memoization on top of the use_context API.
Here's a fully-functional todo app using the use_map API:
```rust
static TodoList: FC<()> = |ctx, props| {
static TodoList: FC<()> = |ctx| {
let todos = use_map(ctx, || HashMap::new());
let input = use_ref(|| None);