Feat: overall API updates

This commit is contained in:
Jonathan Kelley committed 2021-02-12 00:29:46 -05:00
1 parent 125f5426a4
commit f47651b32a
16 files changed
+234 -177

No files matched your search

+3 -5
View File
@@ -23,15 +23,13 @@ impl<P: Properties> Component for FC<P> {
/// The `Properties` trait defines any struct that can be constructed using a combination of default / optional fields.
/// Components take a "properties" object
pub trait Properties: 'static {
fn new() -> Self;
pub trait Properties {
fn call(&self, ptr: *const ()) {}
}
// Auto implement for no-prop components
impl Properties for () {
fn new() -> Self {
()
}
fn call(&self, ptr: *const ()) {}
}
#[cfg(test)]
+1 -1
View File
@@ -27,7 +27,7 @@ use std::{
// todo: force lifetime of source into T as a valid lifetime too
// it's definitely possible, just needs some more messing around
pub struct Context<'src> {
// pub struct Context<'src, PropType> {
pub(crate) scope: &'src Scope,
/// Direct access to the properties used to create this component.
// pub props: &'src PropType,
pub idx: AtomicUsize,
+116
View File
@@ -0,0 +1,116 @@
//! Useful, foundational hooks that 3rd parties can implement.
//! Currently implemented:
//! - use_ref
//! - use_state
pub use use_ref_def::use_ref;
pub use use_state_def::use_state;
mod use_state_def {
use crate::inner::*;
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<'a, T: 'static, F: FnOnce() -> T + 'static>(
ctx: &'_ Context<'a>,
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 crate::inner::*;
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);
}
pub fn current(&self) -> std::cell::Ref<'_, T> {
self._current.borrow()
}
}
/// 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
/// Attach to inner context reference, so context can be consumed
pub fn use_ref<'a, T: 'static>(
ctx: &'_ Context<'a>,
initial_state_fn: impl FnOnce() -> T + 'static,
) -> &'a UseRef<T> {
ctx.use_hook(|| UseRef::new(initial_state_fn()), |state| &*state, |_| {})
}
}
+3
View File
@@ -69,6 +69,7 @@ pub mod component;
pub mod context;
pub mod debug_renderer;
pub mod events;
pub mod hooks;
pub mod nodebuilder;
pub mod nodes;
pub mod scope;
@@ -131,4 +132,6 @@ pub mod prelude {
pub use crate::nodebuilder as builder;
pub use dioxus_core_macro::fc;
pub use dioxus_html_2::html;
pub use crate::hooks::*;
}
+22 -12
View File
@@ -37,7 +37,7 @@ mod vnode {
Suspended,
/// A User-defined componen node (node type COMPONENT_NODE)
Component(VComponent),
Component(VComponent<'src>),
}
impl<'a> VNode<'a> {
@@ -174,11 +174,7 @@ mod velement {
}
/// An event listener.
pub struct Listener<'a>
// pub struct Listener<'a, 'b>
// where
// 'b: 'a + 'static,
{
pub struct Listener<'a> {
/// The type of event to listen for.
pub(crate) event: &'a str,
/// The callback to invoke when the event happens.
@@ -249,19 +245,33 @@ mod vtext {
/// Only supports the functional syntax
mod vcomponent {
use crate::prelude::Properties;
use std::{any::TypeId, fmt, future::Future};
use std::{any::TypeId, fmt, future::Future, marker::PhantomData};
use super::VNode;
#[derive(PartialEq)]
pub struct VComponent {
// props_id: TypeId,
pub struct VComponent<'src> {
_p: PhantomData<&'src ()>,
runner: Box<dyn Properties + 'src>,
caller: *const (),
props_type: TypeId,
}
// impl<'src> PartialEq for CallerSource<'src> {
// fn eq(&self, other: &Self) -> bool {
// todo!()
// }
// }
// caller: Box<dyn Fn
// should we box the caller?
// probably, so we can call it again
//
// props_id: TypeId,
// callerIDs are unsafely coerced to function pointers
// This is okay because #1, we store the props_id and verify and 2# the html! macro rejects components not made this way
//
// Manually constructing the VComponent is not possible from 3rd party crates
}
impl VComponent {
impl<'a> VComponent<'a> {
// /// Construct a VComponent directly from a function component
// /// This should be *very* fast - we store the function pointer and props type ID. It should also be small on the stack
// pub fn from_fn<P: Properties>(f: FC<P>, props: P) -> Self {
+7 -6
View File
@@ -47,18 +47,19 @@ impl Scope {
}
}
pub fn create_context<'runner, T: Properties>(
&'runner mut self,
components: &'runner generational_arena::Arena<Scope>,
props: &'runner T,
pub fn create_context<'a, T: Properties>(
&'a mut self,
components: &'a generational_arena::Arena<Scope>,
props: &'a T,
) -> Context {
// ) -> Context<T> {
//
Context {
scope: &*self,
_p: PhantomData {},
arena: &self.hook_arena,
hooks: &self.hooks,
idx: 0.into(),
// props,
components,
}
}