- integrate subscription service into context.
- Update documentation
This commit is contained in:
Jonathan Kelley committed 2021-02-08 16:57:34 -05:00
1 parent ee23ea6c3a
commit 204f0d9f16
13 files changed
+416 -358

No files matched your search

+125
View File
@@ -0,0 +1,125 @@
#![allow(unused, non_upper_case_globals)]
use dioxus_core::prelude::*;
fn main() {}
static Example: FC<()> = |ctx| {
let (val1, set_val1) = use_state(&ctx, || "b1");
ctx.view(html! {
<div>
<button onclick={move |_| set_val1("b1")}> "Set value to b1" </button>
<button onclick={move |_| set_val1("b2")}> "Set value to b2" </button>
<button onclick={move |_| set_val1("b3")}> "Set value to b3" </button>
<div>
<p> "Value is: {val1}" </p>
</div>
</div>
})
};
use use_state_def::use_state;
mod use_state_def {
use dioxus_core::prelude::*;
use std::{borrow::BorrowMut, cell::RefCell, ops::DerefMut, rc::Rc};
struct UseState<T: 'static> {
new_val: Rc<RefCell<Option<T>>>,
current_val: T,
caller: Box<dyn Fn(T) + 'static>,
}
/// Store state between component renders!
/// When called, this hook retrives a stored value and provides a setter to update that value.
/// When the setter is called, the component is re-ran with the new value.
///
/// This is behaves almost exactly the same way as React's "use_state".
///
/// Usage:
/// ```ignore
/// static Example: FC<()> = |ctx| {
/// let (counter, set_counter) = use_state(ctx, || 0);
/// let increment = || set_couter(counter + 1);
/// let decrement = || set_couter(counter + 1);
///
/// html! {
/// <div>
/// <h1>"Counter: {counter}" </h1>
/// <button onclick={increment}> "Increment" </button>
/// <button onclick={decrement}> "Decrement" </button>
/// </div>
/// }
/// }
/// ```
pub fn use_state<'b, 'a, P: Properties + 'static, T: 'static, F: FnOnce() -> T + 'static>(
ctx: &'b Context<'a, P>,
initial_state_fn: F,
) -> (&'a T, &'a impl Fn(T)) {
ctx.use_hook(
move || UseState {
new_val: Rc::new(RefCell::new(None)),
current_val: initial_state_fn(),
caller: Box::new(|_| println!("setter called!")),
},
move |hook| {
let inner = hook.new_val.clone();
let scheduled_update = ctx.schedule_update();
// get ownership of the new val and replace the current with the new
// -> as_ref -> borrow_mut -> deref_mut -> take
// -> rc -> &RefCell -> RefMut -> &Option<T> -> T
if let Some(new_val) = hook.new_val.as_ref().borrow_mut().deref_mut().take() {
hook.current_val = new_val;
}
// todo: swap out the caller with a subscription call and an internal update
hook.caller = Box::new(move |new_val| {
// update the setter with the new value
let mut new_inner = inner.as_ref().borrow_mut();
*new_inner = Some(new_val);
// Ensure the component gets updated
scheduled_update();
});
// box gets derefed into a ref which is then taken as ref with the hook
(&hook.current_val, &hook.caller)
},
|_| {},
)
}
}
mod use_ref_def {
use dioxus_core::prelude::*;
use std::{borrow::BorrowMut, cell::RefCell, ops::DerefMut, rc::Rc};
pub struct UseRef<T: 'static> {
current: RefCell<T>,
}
impl<T: 'static> UseRef<T> {
fn new(val: T) -> Self {
Self {
current: RefCell::new(val),
}
}
fn modify(&self, modifier: impl FnOnce(&mut T)) {
let mut val = self.current.borrow_mut();
let val_as_ref = val.deref_mut();
modifier(val_as_ref);
}
}
/// Store a mutable value between renders!
/// To read the value, borrow the ref.
/// To change it, use modify.
/// Modifications to this value do not cause updates to the component
pub fn use_ref<'a, P, T: 'static>(
ctx: &'a Context<'a, P>,
initial_state_fn: impl FnOnce() -> T + 'static,
) -> &'a UseRef<T> {
ctx.use_hook(|| UseRef::new(initial_state_fn()), |state| &*state, |_| {})
}
}
+118 -117
View File
@@ -1,136 +1,137 @@
#![allow(unused, non_upper_case_globals, non_snake_case)]
use bumpalo::Bump;
use dioxus_core::nodebuilder::*;
use dioxus_core::prelude::*;
use std::{collections::HashMap, future::Future, marker::PhantomData};
// #![allow(unused, non_upper_case_globals, non_snake_case)]
// use bumpalo::Bump;
// use dioxus_core::nodebuilder::*;
// use dioxus_core::prelude::*;
// use std::{collections::HashMap, future::Future, marker::PhantomData};
fn main() {
let mut vdom = VirtualDom::new_with_props(
component,
Props {
blah: false,
text: "blah".into(),
},
);
fn main() {}
// fn main() {
// let mut vdom = VirtualDom::new_with_props(
// component,
// Props {
// blah: false,
// text: "blah".into(),
// },
// );
vdom.progress();
// vdom.progress();
let somet = String::from("asd");
let text = somet.as_str();
// let somet = String::from("asd");
// let text = somet.as_str();
/*
this could be auto-generated via the macro
this props is allocated in this
but the component and props would like need to be cached
we could box this fn, abstracting away the props requirement and just keep the entrance and allocator requirement
How do we keep cached things around?
Need some sort of caching mechanism
// /*
// this could be auto-generated via the macro
// this props is allocated in this
// but the component and props would like need to be cached
// we could box this fn, abstracting away the props requirement and just keep the entrance and allocator requirement
// How do we keep cached things around?
// Need some sort of caching mechanism
how do we enter into a childscope from a parent scope?
// how do we enter into a childscope from a parent scope?
Problems:
1: Comp props need to be stored somewhere so we can re-evalute components when they receive updates
2: Trees are not evaluated
// Problems:
// 1: Comp props need to be stored somewhere so we can re-evalute components when they receive updates
// 2: Trees are not evaluated
*/
let example_caller = move |ctx: &Bump| {
todo!()
// let p = Props { blah: true, text };
// let c = Context { props: &p };
// let r = component(&c);
};
// */
// let example_caller = move |ctx: &Bump| {
// todo!()
// // let p = Props { blah: true, text };
// // let c = Context { props: &p };
// // let r = component(&c);
// };
// check the edit list
}
// // check the edit list
// }
// ~~~ Text shared between components via props can be done with lifetimes! ~~~
// Super duper efficient :)
struct Props {
blah: bool,
text: String,
// text: &'src str,
}
impl Properties for Props {
fn new() -> Self {
todo!()
}
}
// // ~~~ Text shared between components via props can be done with lifetimes! ~~~
// // Super duper efficient :)
// struct Props {
// blah: bool,
// text: String,
// // text: &'src str,
// }
// impl Properties for Props {
// fn new() -> Self {
// todo!()
// }
// }
fn component<'a>(ctx: Context<'a, Props>) -> VNode<'a> {
// Write asynchronous rendering code that immediately returns a "suspended" VNode
// The concurrent API will then progress this component when the future finishes
// You can suspend the entire component, or just parts of it
let product_list = ctx.suspend(async {
// Suspend the rendering that completes when the future is done
match fetch_data().await {
Ok(data) => html! { <div> "success!" </div>},
Err(_) => html! { <div> "failure :(" </div>},
}
});
// fn component<'a>(ctx: Context<'a, Props>) -> VNode<'a> {
// // Write asynchronous rendering code that immediately returns a "suspended" VNode
// // The concurrent API will then progress this component when the future finishes
// // You can suspend the entire component, or just parts of it
// let product_list = ctx.suspend(async {
// // Suspend the rendering that completes when the future is done
// match fetch_data().await {
// Ok(data) => html! { <div> "success!" </div>},
// Err(_) => html! { <div> "failure :(" </div>},
// }
// });
// todo!()
ctx.view(html! {
<div>
<h1> "Products" </h1>
<button> "hello!" </button>
// Subnodes can even be suspended
// When completely rendered, they won't cause the component itself to re-render, just their slot
// // todo!()
// ctx.view(html! {
// <div>
// <h1> "Products" </h1>
// <button> "hello!" </button>
// // Subnodes can even be suspended
// // When completely rendered, they won't cause the component itself to re-render, just their slot
// <p> { product_list } </p>
</div>
})
}
// // <p> { product_list } </p>
// </div>
// })
// }
fn BuilderComp<'a>(ctx: &'a Context<'a, Props>) -> VNode<'a> {
// VNodes can be constructed via a builder or the html! macro
// However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
// We can "view" them with Context for ultimate speed while inside components
ctx.view(|bump| {
div(bump)
.attr("class", "edit")
.child(text("Hello"))
.child(text(ctx.props.text.as_ref()))
.finish()
})
}
// fn BuilderComp<'a>(ctx: Context<'a, Props>) -> VNode<'a> {
// // VNodes can be constructed via a builder or the html! macro
// // However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
// // We can "view" them with Context for ultimate speed while inside components
// ctx.view(|bump| {
// div(bump)
// .attr("class", "edit")
// .child(text("Hello"))
// .child(text(ctx.props.text.as_ref()))
// .finish()
// })
// }
#[fc]
fn EffcComp(ctx: &Context, name: &str) -> VNode {
// VNodes can be constructed via a builder or the html! macro
// However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
// We can "view" them with Context for ultimate speed while inside components
// use "phase" style allocation;
// #[fc]
// fn EffcComp(ctx: Context, name: &str) -> VNode {
// // VNodes can be constructed via a builder or the html! macro
// // However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
// // We can "view" them with Context for ultimate speed while inside components
// // use "phase" style allocation;
ctx.view(html! {
<div>
// your template goes here
// feel free to directly use "name"
</div>
})
}
// ctx.view(html! {
// <div>
// // your template goes here
// // feel free to directly use "name"
// </div>
// })
// }
fn FullySuspended<'a>(ctx: &'a Context<Props>) -> VNode<'a> {
ctx.suspend(async {
let i: i32 = 0;
// fn FullySuspended<'a>(ctx: &'a Context<Props>) -> VNode<'a> {
// ctx.suspend(async {
// let i: i32 = 0;
// full suspended works great with just returning VNodes!
// Feel free to capture the html! macro directly
// Anything returned here is automatically viewed
let tex = match i {
1 => html! { <div> </div> },
2 => html! { <div> </div> },
_ => html! { <div> </div> },
};
// // full suspended works great with just returning VNodes!
// // Feel free to capture the html! macro directly
// // Anything returned here is automatically viewed
// let tex = match i {
// 1 => html! { <div> </div> },
// 2 => html! { <div> </div> },
// _ => html! { <div> </div> },
// };
if ctx.props.blah {
html! { <div> </div> }
} else {
tex
}
})
}
// if ctx.props.blah {
// html! { <div> </div> }
// } else {
// tex
// }
// })
// }
/// An example of a datafetching service
async fn fetch_data() -> Result<String, ()> {
todo!()
}
// /// An example of a datafetching service
// async fn fetch_data() -> Result<String, ()> {
// todo!()
// }
-185
View File
@@ -1,185 +0,0 @@
use bumpalo::Bump;
use dioxus_core as dioxus;
use dioxus_core::{
prelude::{html, Context, Properties, VElement, VNode, FC},
scope::Scope,
};
use std::{
any::Any,
borrow::BorrowMut,
cell::RefCell,
mem::swap,
ops::{Deref, DerefMut},
rc::Rc,
};
use std::{borrow::Borrow, sync::atomic::AtomicUsize};
use typed_arena::Arena;
fn main() {
let mut scope = Scope::new(component);
(0..5).for_each(|f| {
let ctx = scope.create_context();
component(ctx);
});
}
// we need to do something about props and context being borrowed from different sources....
// kinda anooying
/// use_ref creates a new value when the component is created and then borrows that value on every render
fn component(ctx: Context<()>) -> VNode {
(0..10).for_each(|f| {
let r = use_ref(&ctx, move || f);
});
todo!()
}
pub struct UseRef<T: 'static> {
current: RefCell<T>,
}
impl<T: 'static> UseRef<T> {
fn new(val: T) -> Self {
Self {
current: RefCell::new(val),
}
}
fn modify(&self, modifier: impl FnOnce(&mut T)) {
let mut val = self.current.borrow_mut();
let val_as_ref = val.deref_mut();
modifier(val_as_ref);
}
}
/// Store a mutable value between renders!
/// To read the value, borrow the ref.
/// To change it, use modify.
/// Modifications to this value do not cause updates to the component
pub fn use_ref<'a, P, T: 'static>(
ctx: &'a Context<'a, P>,
initial_state_fn: impl FnOnce() -> T + 'static,
) -> &'a UseRef<T> {
ctx.use_hook(
|| UseRef::new(initial_state_fn()),
|state, _| &*state,
|_| {},
)
}
struct UseState<T> {
new_val: Rc<RefCell<Option<T>>>,
current_val: T,
caller: Box<dyn Fn(T) + 'static>,
}
/// Store state between component renders!
/// When called, this hook retrives a stored value and provides a setter to update that value.
/// When the setter is called, the component is re-ran with the new value.
///
/// This is behaves almost exactly the same way as React's "use_state".
///
/// Usage:
/// ```ignore
/// static Example: FC<()> = |ctx| {
/// let (counter, set_counter) = use_state(ctx, || 0);
/// let increment = || set_couter(counter + 1);
/// let decrement = || set_couter(counter + 1);
///
/// html! {
/// <div>
/// <h1>"Counter: {counter}" </h1>
/// <button onclick={increment}> "Increment" </button>
/// <button onclick={decrement}> "Decrement" </button>
/// </div>
/// }
/// }
/// ```
pub fn use_state<'a, P: Properties + 'static, T: 'static, F: FnOnce() -> T + 'static>(
ctx: &'a Context<'a, P>,
initial_state_fn: F,
) -> (&T, &impl Fn(T)) {
ctx.use_hook(
move || UseState {
new_val: Rc::new(RefCell::new(None)),
current_val: initial_state_fn(),
caller: Box::new(|_| println!("setter called!")),
},
move |hook, updater| {
let inner = hook.new_val.clone();
// todo: swap out the caller with a subscription call and an internal update
hook.caller = Box::new(move |new_val| {
// update the setter with the new value
let mut new_inner = inner.as_ref().borrow_mut();
*new_inner = Some(new_val);
});
// box gets derefed into a ref which is then taken as ref with the hook
(&hook.current_val, &hook.caller)
},
|_| {},
)
}
fn test_use_state(ctx: Context<()>) -> VNode {
let (val, set_val) = use_state(&ctx, || 10);
// gloriousness!
// closures are taken with ref to ctx :)
// Can freely use hooks
let handler_0: _ = || set_val(val + 1);
let handler_1: _ = || set_val(val + 10);
let handler_2: _ = || set_val(val + 100);
// these fns are captured, boxed into the bump arena, and then attached to the listeners
// the vnodes share the lifetime of these closures (and the hook data)
// whenever a listener wakes up, we take the reference directly from the bump arena and, with a small bit
// of unsafe code, execute the associated closure / listener function
// Those vnodes are then tossed out and new ones are installed, meaning and old references (potentially bad)
// are removed and UB is prevented from /affecting/ the program
{
VNode::text("blah")
}
}
#[allow(unused, non_upper_case_globals)]
static UseStateBorrowedFc: FC<()> = |ctx| {
let (val, set_val) = use_state(&ctx, || 0);
let incr = || set_val(val + 1);
let decr = || set_val(val - 1);
ctx.view(html! {
<div>
<nav class="menu">
<button> "Increment" </button>
<button> "Decrement" </button>
</nav>
<p> "Current value: {val}" </p>
</div>
})
};
/// A useful component
/// With some great documentation
/// it practically speaks for itself.
/// It's also only a server component :o
fn use_state_borrowed(ctx: Context<()>) -> VNode {
let (val, set_val) = use_state(&ctx, || 0);
let incr = || set_val(val + 1);
let decr = || set_val(val - 1);
ctx.view(html! {
<div>
<nav class="menu">
<button> "Increment" </button>
<button> "Decrement" </button>
</nav>
<p> "Current value: {val}" </p>
</div>
})
}
// <button onclick=incr> { "Increment" } </button>
// <button onclick=decr> { "Decrement" } </button>