mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 14:44:12 +00:00
wip: merge in some code from the other branch
This commit is contained in:
parent
795a54a2e4
commit
7790750349
12 changed files with 782 additions and 124 deletions
|
@ -2,8 +2,9 @@ fn main() {}
|
|||
|
||||
pub mod dioxus {
|
||||
pub mod prelude {
|
||||
pub trait Properties {
|
||||
pub unsafe trait Properties {
|
||||
type Builder;
|
||||
const CAN_BE_MEMOIZED: bool;
|
||||
fn builder() -> Self::Builder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -679,8 +679,9 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||
}
|
||||
}
|
||||
|
||||
impl #impl_generics dioxus::prelude::Properties for #name #ty_generics{
|
||||
unsafe impl #impl_generics dioxus::prelude::Properties for #name #ty_generics{
|
||||
type Builder = #builder_name #generics_with_empty;
|
||||
const CAN_BE_MEMOIZED: bool = true;
|
||||
fn builder() -> Self::Builder {
|
||||
#name::builder()
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ use generational_arena::Arena;
|
|||
use crate::innerlude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ScopeArena(Rc<RefCell<ScopeArenaInner>>);
|
||||
pub struct ScopeArena(pub Rc<RefCell<ScopeArenaInner>>);
|
||||
|
||||
struct ScopeArenaInner {
|
||||
pub struct ScopeArenaInner {
|
||||
pub(crate) arena: UnsafeCell<Arena<Scope>>,
|
||||
locks: HashMap<ScopeIdx, MutStatus>,
|
||||
}
|
||||
|
@ -61,6 +61,13 @@ impl ScopeArena {
|
|||
// todo!()
|
||||
}
|
||||
|
||||
pub fn try_remove(&mut self, id: ScopeIdx) -> Result<Scope> {
|
||||
let inner = unsafe { &mut *self.0.borrow().arena.get() };
|
||||
inner
|
||||
.remove(id)
|
||||
.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
}
|
||||
|
||||
unsafe fn inner_unchecked<'s>() -> &'s mut Arena<Scope> {
|
||||
todo!()
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@ use crate::innerlude::FC;
|
|||
|
||||
pub type ScopeIdx = generational_arena::Index;
|
||||
|
||||
pub trait Properties: PartialEq {
|
||||
pub unsafe trait Properties: PartialEq {
|
||||
type Builder;
|
||||
const CAN_BE_MEMOIZED: bool;
|
||||
fn builder() -> Self::Builder;
|
||||
}
|
||||
|
||||
|
@ -21,7 +22,8 @@ impl EmptyBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
impl Properties for () {
|
||||
unsafe impl Properties for () {
|
||||
const CAN_BE_MEMOIZED: bool = true;
|
||||
type Builder = EmptyBuilder;
|
||||
|
||||
fn builder() -> Self::Builder {
|
||||
|
|
|
@ -120,7 +120,7 @@ impl<'a> DiffMachine<'a> {
|
|||
When re-entering, we reuse the EditList in DiffState
|
||||
*/
|
||||
match (old, new) {
|
||||
(VNode::Text(VText { text: old_text }), VNode::Text(VText { text: new_text })) => {
|
||||
(VNode::Text(old_text), VNode::Text(new_text)) => {
|
||||
if old_text != new_text {
|
||||
self.change_list.commit_traversal();
|
||||
self.change_list.set_text(new_text);
|
||||
|
@ -157,30 +157,32 @@ impl<'a> DiffMachine<'a> {
|
|||
// self.change_list.commit_traversal();
|
||||
if cold.user_fc == cnew.user_fc {
|
||||
// todo: create a stable addr
|
||||
let caller = Rc::downgrade(&cnew.caller);
|
||||
let id = cold.stable_addr.borrow().unwrap();
|
||||
*cnew.stable_addr.borrow_mut() = Some(id);
|
||||
*cnew.ass_scope.borrow_mut() = *cold.ass_scope.borrow();
|
||||
// let caller = Rc::downgrade(&cnew.caller);
|
||||
// let id = cold.stable_addr.borrow().unwrap();
|
||||
// *cnew.stable_addr.borrow_mut() = Some(id);
|
||||
// *cnew.ass_scope.borrow_mut() = *cold.ass_scope.borrow();
|
||||
|
||||
let scope = Rc::downgrade(&cold.ass_scope);
|
||||
self.lifecycle_events
|
||||
.push_back(LifeCycleEvent::PropsChanged {
|
||||
caller,
|
||||
root_id: id,
|
||||
stable_scope_addr: scope,
|
||||
});
|
||||
// let scope = Rc::downgrade(&cold.ass_scope);
|
||||
todo!()
|
||||
// self.lifecycle_events
|
||||
// .push_back(LifeCycleEvent::PropsChanged {
|
||||
// caller,
|
||||
// root_id: id,
|
||||
// stable_scope_addr: scope,
|
||||
// });
|
||||
} else {
|
||||
let caller = Rc::downgrade(&cnew.caller);
|
||||
let id = cold.stable_addr.borrow().unwrap();
|
||||
let old_scope = Rc::downgrade(&cold.ass_scope);
|
||||
let new_scope = Rc::downgrade(&cnew.ass_scope);
|
||||
// let caller = Rc::downgrade(&cnew.caller);
|
||||
// let id = cold.stable_addr.borrow().unwrap();
|
||||
// let old_scope = Rc::downgrade(&cold.ass_scope);
|
||||
// let new_scope = Rc::downgrade(&cnew.ass_scope);
|
||||
|
||||
self.lifecycle_events.push_back(LifeCycleEvent::Replace {
|
||||
caller,
|
||||
root_id: id,
|
||||
old_scope,
|
||||
new_scope,
|
||||
});
|
||||
todo!()
|
||||
// self.lifecycle_events.push_back(LifeCycleEvent::Replace {
|
||||
// caller,
|
||||
// root_id: id,
|
||||
// old_scope,
|
||||
// new_scope,
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,7 +219,7 @@ impl<'a> DiffMachine<'a> {
|
|||
fn create(&mut self, node: &VNode<'a>) {
|
||||
debug_assert!(self.change_list.traversal_is_committed());
|
||||
match node {
|
||||
VNode::Text(VText { text }) => {
|
||||
VNode::Text(text) => {
|
||||
self.change_list.create_text_node(text);
|
||||
}
|
||||
VNode::Element(&VElement {
|
||||
|
@ -252,7 +254,7 @@ impl<'a> DiffMachine<'a> {
|
|||
// text content, and finally (3) append the text node to this
|
||||
// parent.
|
||||
if children.len() == 1 {
|
||||
if let VNode::Text(VText { text }) = children[0] {
|
||||
if let VNode::Text(text) = children[0] {
|
||||
self.change_list.set_text(text);
|
||||
return;
|
||||
}
|
||||
|
@ -268,18 +270,20 @@ impl<'a> DiffMachine<'a> {
|
|||
todo: integrate re-entrace
|
||||
*/
|
||||
VNode::Component(component) => {
|
||||
self.change_list
|
||||
.create_text_node("placeholder for vcomponent");
|
||||
todo!()
|
||||
// self.change_list
|
||||
// .create_text_node("placeholder for vcomponent");
|
||||
|
||||
let id = get_id();
|
||||
*component.stable_addr.as_ref().borrow_mut() = Some(id);
|
||||
self.change_list.save_known_root(id);
|
||||
let scope = Rc::downgrade(&component.ass_scope);
|
||||
self.lifecycle_events.push_back(LifeCycleEvent::Mount {
|
||||
caller: Rc::downgrade(&component.caller),
|
||||
root_id: id,
|
||||
stable_scope_addr: scope,
|
||||
});
|
||||
// let id = get_id();
|
||||
// *component.stable_addr.as_ref().borrow_mut() = Some(id);
|
||||
// self.change_list.save_known_root(id);
|
||||
// let scope = Rc::downgrade(&component.ass_scope);
|
||||
// todo!()
|
||||
// self.lifecycle_events.push_back(LifeCycleEvent::Mount {
|
||||
// caller: Rc::downgrade(&component.caller),
|
||||
// root_id: id,
|
||||
// stable_scope_addr: scope,
|
||||
// });
|
||||
}
|
||||
VNode::Suspended => {
|
||||
todo!("Creation of VNode::Suspended not yet supported")
|
||||
|
@ -422,14 +426,11 @@ impl<'a> DiffMachine<'a> {
|
|||
|
||||
if new.len() == 1 {
|
||||
match (old.first(), &new[0]) {
|
||||
(
|
||||
Some(&VNode::Text(VText { text: old_text })),
|
||||
&VNode::Text(VText { text: new_text }),
|
||||
) if old_text == new_text => {
|
||||
(Some(&VNode::Text(old_text)), &VNode::Text(new_text)) if old_text == new_text => {
|
||||
// Don't take this fast path...
|
||||
}
|
||||
|
||||
(_, &VNode::Text(VText { text })) => {
|
||||
(_, &VNode::Text(text)) => {
|
||||
self.change_list.commit_traversal();
|
||||
self.change_list.set_text(text);
|
||||
// for o in old {
|
||||
|
@ -979,11 +980,12 @@ impl<'a> DiffMachine<'a> {
|
|||
// self.change_list
|
||||
// .create_text_node("placeholder for vcomponent");
|
||||
|
||||
let root_id = vcomp.stable_addr.as_ref().borrow().unwrap();
|
||||
self.lifecycle_events.push_back(LifeCycleEvent::Remove {
|
||||
root_id,
|
||||
stable_scope_addr: Rc::downgrade(&vcomp.ass_scope),
|
||||
})
|
||||
todo!()
|
||||
// let root_id = vcomp.stable_addr.as_ref().borrow().unwrap();
|
||||
// self.lifecycle_events.push_back(LifeCycleEvent::Remove {
|
||||
// root_id,
|
||||
// stable_scope_addr: Rc::downgrade(&vcomp.ass_scope),
|
||||
// })
|
||||
// let id = get_id();
|
||||
// *component.stable_addr.as_ref().borrow_mut() = Some(id);
|
||||
// self.change_list.save_known_root(id);
|
||||
|
|
|
@ -86,6 +86,8 @@ pub mod virtual_dom; // Most fun logic starts here, manages the lifecycle and su
|
|||
pub mod builder {
|
||||
pub use super::nodebuilder::*;
|
||||
}
|
||||
pub mod scope;
|
||||
pub mod support;
|
||||
|
||||
// types used internally that are important
|
||||
pub(crate) mod innerlude {
|
||||
|
|
|
@ -27,7 +27,7 @@ pub enum VNode<'src> {
|
|||
Element(&'src VElement<'src>),
|
||||
|
||||
/// A text node (node type `TEXT_NODE`).
|
||||
Text(VText<'src>),
|
||||
Text(&'src str),
|
||||
|
||||
/// A fragment is a "virtual position" in the DOM
|
||||
/// Fragments may have children and keys
|
||||
|
@ -46,11 +46,11 @@ pub enum VNode<'src> {
|
|||
impl<'a> Clone for VNode<'a> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
VNode::Element(el) => VNode::Element(el),
|
||||
VNode::Text(origi) => VNode::Text(VText { text: origi.text }),
|
||||
VNode::Fragment(frag) => VNode::Fragment(frag),
|
||||
VNode::Element(element) => VNode::Element(element),
|
||||
VNode::Text(text) => VNode::Text(text),
|
||||
VNode::Fragment(fragment) => VNode::Fragment(fragment),
|
||||
VNode::Component(component) => VNode::Component(component),
|
||||
VNode::Suspended => VNode::Suspended,
|
||||
VNode::Component(c) => VNode::Component(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ impl<'a> VNode<'a> {
|
|||
/// Construct a new text node with the given text.
|
||||
#[inline]
|
||||
pub fn text(text: &'a str) -> VNode<'a> {
|
||||
VNode::Text(VText { text })
|
||||
VNode::Text(text)
|
||||
}
|
||||
|
||||
pub fn text_args(bump: &'a Bump, args: Arguments) -> VNode<'a> {
|
||||
|
@ -210,47 +210,31 @@ impl<'a> NodeKey<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct VText<'bump> {
|
||||
pub text: &'bump str,
|
||||
}
|
||||
|
||||
impl<'b> Clone for VText<'b> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { text: self.text }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VText<'a> {
|
||||
// / Create an new `VText` instance with the specified text.
|
||||
pub fn new(text: &'a str) -> Self {
|
||||
VText { text: text.into() }
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================
|
||||
// Custom components
|
||||
// ==============================
|
||||
|
||||
/// Virtual Components for custom user-defined components
|
||||
/// Only supports the functional syntax
|
||||
pub type StableScopeAddres = RefCell<Option<u32>>;
|
||||
pub type VCompAssociatedScope = RefCell<Option<ScopeIdx>>;
|
||||
pub type StableScopeAddres = Option<u32>;
|
||||
pub type VCompAssociatedScope = Option<ScopeIdx>;
|
||||
|
||||
pub struct VComponent<'src> {
|
||||
pub key: NodeKey<'src>,
|
||||
|
||||
pub stable_addr: Rc<StableScopeAddres>,
|
||||
pub ass_scope: Rc<VCompAssociatedScope>,
|
||||
pub stable_addr: RefCell<StableScopeAddres>,
|
||||
pub ass_scope: RefCell<VCompAssociatedScope>,
|
||||
|
||||
// pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
|
||||
pub caller: Rc<dyn Fn(&Scope) -> VNode + 'src>,
|
||||
|
||||
pub children: &'src [VNode<'src>],
|
||||
|
||||
pub comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
|
||||
|
||||
// a pointer into the bump arena (given by the 'src lifetime)
|
||||
// raw_props: Box<dyn Any>,
|
||||
// raw_props: *const (),
|
||||
raw_props: *const (),
|
||||
|
||||
// a pointer to the raw fn typ
|
||||
pub user_fc: *const (),
|
||||
|
@ -264,7 +248,8 @@ impl<'a> VComponent<'a> {
|
|||
// TODO: lift the requirement that props need to be static
|
||||
// we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
|
||||
|
||||
pub fn new<P: Properties>(
|
||||
pub fn new<P: Properties + 'a>(
|
||||
bump: &'a Bump,
|
||||
component: FC<P>,
|
||||
// props: bumpalo::boxed::Box<'a, P>,
|
||||
props: P,
|
||||
|
@ -273,25 +258,32 @@ impl<'a> VComponent<'a> {
|
|||
// pub fn new<P: Properties + 'a>(component: FC<P>, props: P, key: Option<&'a str>) -> Self {
|
||||
// let bad_props = unsafe { transmogrify(props) };
|
||||
let caller_ref = component as *const ();
|
||||
let props = bump.alloc(props);
|
||||
|
||||
// let raw_props = props as *const P as *const ();
|
||||
let raw_props = props as *const P as *const ();
|
||||
|
||||
// let props_comparator = move |other: &VComponent| {
|
||||
// // Safety:
|
||||
// // We are guaranteed that the props will be of the same type because
|
||||
// // there is no way to create a VComponent other than this `new` method.
|
||||
// //
|
||||
// // Therefore, if the render functions are identical (by address), then so will be
|
||||
// // props type paramter (because it is the same render function). Therefore, we can be
|
||||
// // sure
|
||||
// if caller_ref == other.user_fc {
|
||||
// let g = other.raw_ctx.downcast_ref::<P>().unwrap();
|
||||
// // let real_other = unsafe { &*(other.raw_props as *const _ as *const P) };
|
||||
// &props == g
|
||||
// } else {
|
||||
// false
|
||||
// }
|
||||
// };
|
||||
let comparator: Option<&dyn Fn(&VComponent) -> bool> = {
|
||||
if P::CAN_BE_MEMOIZED {
|
||||
Some(bump.alloc(move |other: &VComponent| {
|
||||
// Safety:
|
||||
// We are guaranteed that the props will be of the same type because
|
||||
// there is no way to create a VComponent other than this `new` method.
|
||||
//
|
||||
// Therefore, if the render functions are identical (by address), then so will be
|
||||
// props type paramter (because it is the same render function). Therefore, we can be
|
||||
// sure
|
||||
if caller_ref == other.user_fc {
|
||||
// let g = other.raw_ctx.downcast_ref::<P>().unwrap();
|
||||
let real_other = unsafe { &*(other.raw_props as *const _ as *const P) };
|
||||
&props == &real_other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// let prref: &'a P = props.as_ref();
|
||||
|
||||
|
@ -313,16 +305,18 @@ impl<'a> VComponent<'a> {
|
|||
None => NodeKey(None),
|
||||
};
|
||||
|
||||
// raw_props: Box::new(props),
|
||||
// comparator: Rc::new(props_comparator),
|
||||
Self {
|
||||
key,
|
||||
ass_scope: Rc::new(RefCell::new(None)),
|
||||
ass_scope: RefCell::new(None),
|
||||
user_fc: caller_ref,
|
||||
// raw_props: Box::new(props),
|
||||
comparator,
|
||||
raw_props,
|
||||
_p: PhantomData,
|
||||
children: &[],
|
||||
caller,
|
||||
// comparator: Rc::new(props_comparator),
|
||||
stable_addr: Rc::new(RefCell::new(None)),
|
||||
stable_addr: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,6 +146,7 @@ pub struct EditMachine<'lock> {
|
|||
pub traversal: Traversal,
|
||||
next_temporary: u32,
|
||||
forcing_new_listeners: bool,
|
||||
pub cur_height: u32,
|
||||
|
||||
// // if the current node is a "known" node
|
||||
// // any actions that modify this node should update the mapping
|
||||
|
@ -158,6 +159,7 @@ impl<'lock> EditMachine<'lock> {
|
|||
Self {
|
||||
// current_known: None,
|
||||
traversal: Traversal::new(),
|
||||
cur_height: 0,
|
||||
next_temporary: 0,
|
||||
forcing_new_listeners: false,
|
||||
emitter: EditList::<'lock>::default(),
|
||||
|
|
488
packages/core/src/scope.rs
Normal file
488
packages/core/src/scope.rs
Normal file
|
@ -0,0 +1,488 @@
|
|||
use crate::{arena::ScopeArena, innerlude::*};
|
||||
use bumpalo::Bump;
|
||||
use generational_arena::Arena;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
fmt::Debug,
|
||||
future::Future,
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
/// Every component in Dioxus is represented by a `Scope`.
|
||||
///
|
||||
/// Scopes contain the state for hooks, the component's props, and other lifecycle information.
|
||||
///
|
||||
/// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
|
||||
/// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
|
||||
pub struct Scope {
|
||||
// The parent's scope ID
|
||||
pub parent: Option<ScopeIdx>,
|
||||
|
||||
// IDs of children that this scope has created
|
||||
// This enables us to drop the children and their children when this scope is destroyed
|
||||
pub(crate) descendents: RefCell<HashSet<ScopeIdx>>,
|
||||
|
||||
pub(crate) child_nodes: &'static [VNode<'static>],
|
||||
|
||||
// A reference to the list of components.
|
||||
// This lets us traverse the component list whenever we need to access our parent or children.
|
||||
pub(crate) arena_link: ScopeArena,
|
||||
|
||||
pub shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
||||
|
||||
// Our own ID accessible from the component map
|
||||
pub arena_idx: ScopeIdx,
|
||||
|
||||
pub height: u32,
|
||||
|
||||
pub event_channel: Rc<dyn Fn() + 'static>,
|
||||
|
||||
// pub event_queue: EventQueue,
|
||||
pub caller: Weak<OpaqueComponent<'static>>,
|
||||
|
||||
pub hookidx: RefCell<usize>,
|
||||
|
||||
// ==========================
|
||||
// slightly unsafe stuff
|
||||
// ==========================
|
||||
// an internal, highly efficient storage of vnodes
|
||||
pub frames: ActiveFrame,
|
||||
|
||||
// These hooks are actually references into the hook arena
|
||||
// These two could be combined with "OwningRef" to remove unsafe usage
|
||||
// or we could dedicate a tiny bump arena just for them
|
||||
// could also use ourborous
|
||||
hooks: RefCell<Vec<Hook>>,
|
||||
|
||||
// Unsafety:
|
||||
// - is self-refenrential and therefore needs to point into the bump
|
||||
// Stores references into the listeners attached to the vnodes
|
||||
// NEEDS TO BE PRIVATE
|
||||
pub(crate) listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
|
||||
}
|
||||
|
||||
// We need to pin the hook so it doesn't move as we initialize the list of hooks
|
||||
type Hook = Pin<Box<dyn std::any::Any>>;
|
||||
type EventChannel = Rc<dyn Fn()>;
|
||||
|
||||
impl Scope {
|
||||
// we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
|
||||
// we are going to break this lifetime by force in order to save it on ourselves.
|
||||
// To make sure that the lifetime isn't truly broken, we receive a Weak RC so we can't keep it around after the parent dies.
|
||||
// This should never happen, but is a good check to keep around
|
||||
//
|
||||
// Scopes cannot be made anywhere else except for this file
|
||||
// Therefore, their lifetimes are connected exclusively to the virtual dom
|
||||
pub(crate) fn new<'creator_node>(
|
||||
caller: Weak<OpaqueComponent<'creator_node>>,
|
||||
arena_idx: ScopeIdx,
|
||||
parent: Option<ScopeIdx>,
|
||||
height: u32,
|
||||
event_channel: EventChannel,
|
||||
arena_link: ScopeArena,
|
||||
child_nodes: &'creator_node [VNode<'creator_node>],
|
||||
) -> Self {
|
||||
log::debug!(
|
||||
"New scope created, height is {}, idx is {:?}",
|
||||
height,
|
||||
arena_idx
|
||||
);
|
||||
|
||||
// The function to run this scope is actually located in the parent's bump arena.
|
||||
// Every time the parent is updated, that function is invalidated via double-buffering wiping the old frame.
|
||||
// If children try to run this invalid caller, it *will* result in UB.
|
||||
//
|
||||
// During the lifecycle progression process, this caller will need to be updated. Right now,
|
||||
// until formal safety abstractions are implemented, we will just use unsafe to "detach" the caller
|
||||
// lifetime from the bump arena, exposing ourselves to this potential for invalidation. Truthfully,
|
||||
// this is a bit of a hack, but will remain this way until we've figured out a cleaner solution.
|
||||
//
|
||||
// Not the best solution, so TODO on removing this in favor of a dedicated resource abstraction.
|
||||
let caller = unsafe {
|
||||
std::mem::transmute::<
|
||||
Weak<OpaqueComponent<'creator_node>>,
|
||||
Weak<OpaqueComponent<'static>>,
|
||||
>(caller)
|
||||
};
|
||||
|
||||
Self {
|
||||
child_nodes: &[],
|
||||
caller,
|
||||
parent,
|
||||
arena_idx,
|
||||
height,
|
||||
event_channel,
|
||||
arena_link,
|
||||
frames: ActiveFrame::new(),
|
||||
hooks: Default::default(),
|
||||
shared_contexts: Default::default(),
|
||||
listeners: Default::default(),
|
||||
hookidx: Default::default(),
|
||||
descendents: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_caller<'creator_node>(&mut self, caller: Weak<OpaqueComponent<'creator_node>>) {
|
||||
let broken_caller = unsafe {
|
||||
std::mem::transmute::<
|
||||
Weak<OpaqueComponent<'creator_node>>,
|
||||
Weak<OpaqueComponent<'static>>,
|
||||
>(caller)
|
||||
};
|
||||
|
||||
self.caller = broken_caller;
|
||||
}
|
||||
|
||||
/// Create a new context and run the component with references from the Virtual Dom
|
||||
/// This function downcasts the function pointer based on the stored props_type
|
||||
///
|
||||
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
|
||||
pub fn run_scope<'sel>(&'sel mut self) -> Result<()> {
|
||||
// Cycle to the next frame and then reset it
|
||||
// This breaks any latent references, invalidating every pointer referencing into it.
|
||||
self.frames.next().bump.reset();
|
||||
|
||||
// Remove all the outdated listeners
|
||||
//
|
||||
self.listeners
|
||||
.try_borrow_mut()
|
||||
.ok()
|
||||
.ok_or(Error::FatalInternal("Borrowing listener failed"))?
|
||||
.drain(..);
|
||||
|
||||
*self.hookidx.borrow_mut() = 0;
|
||||
|
||||
let caller = self
|
||||
.caller
|
||||
.upgrade()
|
||||
.ok_or(Error::FatalInternal("Failed to get caller"))?;
|
||||
|
||||
// Cast the caller ptr from static to one with our own reference
|
||||
let c2: &OpaqueComponent<'static> = caller.as_ref();
|
||||
let c3: &OpaqueComponent<'_> = unsafe { std::mem::transmute(c2) };
|
||||
|
||||
let unsafe_head = unsafe { self.own_vnodes(c3) };
|
||||
|
||||
self.frames.cur_frame_mut().head_node = unsafe_head;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// this is its own function so we can preciesly control how lifetimes flow
|
||||
unsafe fn own_vnodes<'a>(&'a self, f: &OpaqueComponent<'a>) -> VNode<'static> {
|
||||
let new_head: VNode<'a> = f(self);
|
||||
let out: VNode<'static> = std::mem::transmute(new_head);
|
||||
out
|
||||
}
|
||||
|
||||
// A safe wrapper around calling listeners
|
||||
// calling listeners will invalidate the list of listeners
|
||||
// The listener list will be completely drained because the next frame will write over previous listeners
|
||||
pub fn call_listener(&mut self, trigger: EventTrigger) -> Result<()> {
|
||||
let EventTrigger {
|
||||
listener_id, event, ..
|
||||
} = trigger;
|
||||
//
|
||||
unsafe {
|
||||
// Convert the raw ptr into an actual object
|
||||
// This operation is assumed to be safe
|
||||
let listener_fn = self
|
||||
.listeners
|
||||
.try_borrow()
|
||||
.ok()
|
||||
.ok_or(Error::FatalInternal("Borrowing listener failed"))?
|
||||
.get(listener_id as usize)
|
||||
.ok_or(Error::FatalInternal("Event should exist if triggered"))?
|
||||
.as_ref()
|
||||
.ok_or(Error::FatalInternal("Raw event ptr is invalid"))?;
|
||||
|
||||
// Run the callback with the user event
|
||||
listener_fn(event);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
||||
self.frames.current_head_node()
|
||||
}
|
||||
|
||||
pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
||||
self.frames.prev_head_node()
|
||||
}
|
||||
|
||||
pub fn cur_frame(&self) -> &BumpFrame {
|
||||
self.frames.cur_frame()
|
||||
}
|
||||
}
|
||||
|
||||
/// Components in Dioxus use the "Context" object to interact with their lifecycle.
|
||||
/// This lets components schedule updates, integrate hooks, and expose their context via the context api.
|
||||
///
|
||||
/// Properties passed down from the parent component are also directly accessible via the exposed "props" field.
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[derive(Properties)]
|
||||
/// struct Props {
|
||||
/// name: String
|
||||
///
|
||||
/// }
|
||||
///
|
||||
/// fn example(ctx: Context, props: &Props -> VNode {
|
||||
/// html! {
|
||||
/// <div> "Hello, {ctx.ctx.name}" </div>
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
// 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, T> {
|
||||
pub props: &'src T,
|
||||
pub scope: &'src Scope,
|
||||
}
|
||||
|
||||
impl<'src, T> Copy for Context<'src, T> {}
|
||||
impl<'src, T> Clone for Context<'src, T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
props: self.props,
|
||||
scope: self.scope,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for Context<'a, T> {
|
||||
type Target = &'a T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.props
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src, T> Scoped<'src> for Context<'src, T> {
|
||||
fn get_scope(&self) -> &'src Scope {
|
||||
self.scope
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Scoped<'src>: Sized {
|
||||
fn get_scope(&self) -> &'src Scope;
|
||||
|
||||
/// Access the children elements passed into the component
|
||||
fn children(&self) -> &'src [VNode<'src>] {
|
||||
// We're re-casting the nodes back out
|
||||
// They don't really have a static lifetime
|
||||
unsafe {
|
||||
let scope = self.get_scope();
|
||||
let nodes: &'src [VNode<'static>] = scope.child_nodes;
|
||||
|
||||
// cast the lifetime back correctly
|
||||
std::mem::transmute(nodes)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a subscription that schedules a future render for the reference component
|
||||
fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
|
||||
self.get_scope().event_channel.clone()
|
||||
}
|
||||
|
||||
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
||||
///
|
||||
/// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn Component(ctx: Context<()>) -> VNode {
|
||||
/// // Lazy assemble the VNode tree
|
||||
/// let lazy_tree = html! {<div> "Hello World" </div>};
|
||||
///
|
||||
/// // Actually build the tree and allocate it
|
||||
/// ctx.render(lazy_tree)
|
||||
/// }
|
||||
///```
|
||||
fn render<'a, F: for<'b> FnOnce(&'b NodeCtx<'src>) -> VNode<'src> + 'src + 'a>(
|
||||
self,
|
||||
lazy_nodes: LazyNodes<'src, F>,
|
||||
) -> VNode<'src> {
|
||||
lazy_nodes.into_vnode(&NodeCtx {
|
||||
scope_ref: self.get_scope(),
|
||||
listener_id: 0.into(),
|
||||
})
|
||||
}
|
||||
|
||||
// impl<'scope> Context<'scope> {
|
||||
/// Store a value between renders
|
||||
///
|
||||
/// - Initializer: closure used to create the initial hook state
|
||||
/// - Runner: closure used to output a value every time the hook is used
|
||||
/// - Cleanup: closure used to teardown the hook once the dom is cleaned up
|
||||
///
|
||||
/// ```ignore
|
||||
/// // use_ref is the simplest way of storing a value between renders
|
||||
/// 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(),
|
||||
/// |_| {},
|
||||
/// )
|
||||
/// }
|
||||
/// ```
|
||||
fn use_hook<InternalHookState: 'static, Output: 'src>(
|
||||
&self,
|
||||
|
||||
// The closure that builds the hook state
|
||||
initializer: impl FnOnce() -> InternalHookState,
|
||||
|
||||
// The closure that takes the hookstate and returns some value
|
||||
runner: impl FnOnce(&'src mut InternalHookState) -> Output,
|
||||
|
||||
// The closure that cleans up whatever mess is left when the component gets torn down
|
||||
// TODO: add this to the "clean up" group for when the component is dropped
|
||||
_cleanup: impl FnOnce(InternalHookState),
|
||||
) -> Output {
|
||||
let scope = self.get_scope();
|
||||
|
||||
let idx = *scope.hookidx.borrow();
|
||||
|
||||
// Grab out the hook list
|
||||
let mut hooks = scope.hooks.borrow_mut();
|
||||
|
||||
// If the idx is the same as the hook length, then we need to add the current hook
|
||||
if idx >= hooks.len() {
|
||||
let new_state = initializer();
|
||||
hooks.push(Box::pin(new_state));
|
||||
}
|
||||
|
||||
*scope.hookidx.borrow_mut() += 1;
|
||||
|
||||
let stable_ref = hooks
|
||||
.get_mut(idx)
|
||||
.expect("Should not fail, idx is validated")
|
||||
.as_mut();
|
||||
|
||||
let pinned_state = unsafe { Pin::get_unchecked_mut(stable_ref) };
|
||||
|
||||
let internal_state = pinned_state.downcast_mut::<InternalHookState>().expect(
|
||||
r###"
|
||||
Unable to retrive the hook that was initialized in this index.
|
||||
Consult the `rules of hooks` to understand how to use hooks properly.
|
||||
|
||||
You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
|
||||
Any function prefixed with "use" should not be called conditionally.
|
||||
"###,
|
||||
);
|
||||
|
||||
// We extend the lifetime of the internal state
|
||||
runner(unsafe { &mut *(internal_state as *mut _) })
|
||||
}
|
||||
|
||||
/// This hook enables the ability to expose state to children further down the VirtualDOM Tree.
|
||||
///
|
||||
/// This is a hook, so it may not be called conditionally!
|
||||
///
|
||||
/// The init method is ran *only* on first use, otherwise it is ignored. However, it uses hooks (ie `use`)
|
||||
/// so don't put it in a conditional.
|
||||
///
|
||||
/// When the component is dropped, so is the context. Be aware of this behavior when consuming
|
||||
/// the context via Rc/Weak.
|
||||
///
|
||||
///
|
||||
///
|
||||
fn use_create_context<T: 'static>(&self, init: impl Fn() -> T) {
|
||||
let scope = self.get_scope();
|
||||
let mut ctxs = scope.shared_contexts.borrow_mut();
|
||||
let ty = TypeId::of::<T>();
|
||||
|
||||
let is_initialized = self.use_hook(
|
||||
|| false,
|
||||
|s| {
|
||||
let i = s.clone();
|
||||
*s = true;
|
||||
i
|
||||
},
|
||||
|_| {},
|
||||
);
|
||||
|
||||
match (is_initialized, ctxs.contains_key(&ty)) {
|
||||
// Do nothing, already initialized and already exists
|
||||
(true, true) => {}
|
||||
|
||||
// Needs to be initialized
|
||||
(false, false) => {
|
||||
log::debug!("Initializing context...");
|
||||
ctxs.insert(ty, Rc::new(init()));
|
||||
}
|
||||
|
||||
_ => debug_assert!(false, "Cannot initialize two contexts of the same type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// There are hooks going on here!
|
||||
fn use_context<T: 'static>(&self) -> &'src Rc<T> {
|
||||
self.try_use_context().unwrap()
|
||||
}
|
||||
|
||||
/// Uses a context, storing the cached value around
|
||||
fn try_use_context<T: 'static>(&self) -> Result<&'src Rc<T>> {
|
||||
struct UseContextHook<C> {
|
||||
par: Option<Rc<C>>,
|
||||
we: Option<Weak<C>>,
|
||||
}
|
||||
|
||||
self.use_hook(
|
||||
move || UseContextHook {
|
||||
par: None as Option<Rc<T>>,
|
||||
we: None as Option<Weak<T>>,
|
||||
},
|
||||
move |hook| {
|
||||
let scope = self.get_scope();
|
||||
let mut scope = Some(scope);
|
||||
|
||||
if let Some(we) = &hook.we {
|
||||
if let Some(re) = we.upgrade() {
|
||||
hook.par = Some(re);
|
||||
return Ok(hook.par.as_ref().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
let ty = TypeId::of::<T>();
|
||||
while let Some(inner) = scope {
|
||||
log::debug!("Searching {:#?} for valid shared_context", inner.arena_idx);
|
||||
let shared_contexts = inner.shared_contexts.borrow();
|
||||
|
||||
if let Some(shared_ctx) = shared_contexts.get(&ty) {
|
||||
log::debug!("found matching ctx");
|
||||
let rc = shared_ctx
|
||||
.clone()
|
||||
.downcast::<T>()
|
||||
.expect("Should not fail, already validated the type from the hashmap");
|
||||
|
||||
hook.we = Some(Rc::downgrade(&rc));
|
||||
hook.par = Some(rc);
|
||||
return Ok(hook.par.as_ref().unwrap());
|
||||
} else {
|
||||
match inner.parent {
|
||||
Some(parent_id) => {
|
||||
let parent = inner
|
||||
.arena_link
|
||||
.try_get(parent_id)
|
||||
.map_err(|_| Error::FatalInternal("Failed to find parent"))?;
|
||||
|
||||
scope = Some(parent);
|
||||
}
|
||||
None => return Err(Error::MissingSharedContext),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::MissingSharedContext)
|
||||
},
|
||||
|_| {},
|
||||
)
|
||||
}
|
||||
}
|
166
packages/core/src/support.rs
Normal file
166
packages/core/src/support.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
pub use crate::scope::*;
|
||||
use crate::{arena::ScopeArena, innerlude::*};
|
||||
use bumpalo::Bump;
|
||||
use generational_arena::Arena;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
fmt::Debug,
|
||||
future::Future,
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
// We actually allocate the properties for components in their parent's properties
|
||||
// We then expose a handle to use those props for render in the form of "OpaqueComponent"
|
||||
pub(crate) type OpaqueComponent<'e> = dyn Fn(&'e Scope) -> VNode<'e> + 'e;
|
||||
// pub(crate) type OpaqueComponent<'e> = dyn for<'b> Fn(&'b Scope) -> VNode<'b> + 'e;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Default)]
|
||||
pub(crate) struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
|
||||
|
||||
impl EventQueue {
|
||||
pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
|
||||
let inner = self.clone();
|
||||
let marker = HeightMarker { height, idx };
|
||||
Rc::new(move || inner.0.as_ref().borrow_mut().push(marker))
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper type that lets scopes be ordered by their height
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct HeightMarker {
|
||||
pub idx: ScopeIdx,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl Ord for HeightMarker {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.height.cmp(&other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for HeightMarker {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
// NodeCtx is used to build VNodes in the component's memory space.
|
||||
// This struct adds metadata to the final VNode about listeners, attributes, and children
|
||||
#[derive(Clone)]
|
||||
pub struct NodeCtx<'a> {
|
||||
pub scope_ref: &'a Scope,
|
||||
pub listener_id: RefCell<usize>,
|
||||
}
|
||||
|
||||
impl<'a> NodeCtx<'a> {
|
||||
pub fn bump(&self) -> &'a Bump {
|
||||
&self.scope_ref.cur_frame().bump
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for NodeCtx<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Hash)]
|
||||
pub struct ContextId {
|
||||
// Which component is the scope in
|
||||
original: ScopeIdx,
|
||||
|
||||
// What's the height of the scope
|
||||
height: u32,
|
||||
|
||||
// Which scope is it (in order)
|
||||
id: u32,
|
||||
}
|
||||
|
||||
pub struct ActiveFrame {
|
||||
// We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
|
||||
pub generation: RefCell<usize>,
|
||||
|
||||
// The double-buffering situation that we will use
|
||||
pub frames: [BumpFrame; 2],
|
||||
}
|
||||
|
||||
pub struct BumpFrame {
|
||||
pub bump: Bump,
|
||||
pub head_node: VNode<'static>,
|
||||
}
|
||||
|
||||
impl ActiveFrame {
|
||||
pub fn new() -> Self {
|
||||
Self::from_frames(
|
||||
BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: VNode::text(""),
|
||||
},
|
||||
BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: VNode::text(""),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
|
||||
Self {
|
||||
generation: 0.into(),
|
||||
frames: [a, b],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cur_frame(&self) -> &BumpFrame {
|
||||
match *self.generation.borrow() & 1 == 0 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
}
|
||||
}
|
||||
pub(crate) fn cur_frame_mut(&mut self) -> &mut BumpFrame {
|
||||
match *self.generation.borrow() & 1 == 0 {
|
||||
true => &mut self.frames[0],
|
||||
false => &mut self.frames[1],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
|
||||
let raw_node = match *self.generation.borrow() & 1 == 0 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
};
|
||||
|
||||
// Give out our self-referential item with our own borrowed lifetime
|
||||
unsafe {
|
||||
let unsafe_head = &raw_node.head_node;
|
||||
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
||||
safe_node
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
|
||||
let raw_node = match *self.generation.borrow() & 1 != 0 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
};
|
||||
|
||||
// Give out our self-referential item with our own borrowed lifetime
|
||||
unsafe {
|
||||
let unsafe_head = &raw_node.head_node;
|
||||
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
||||
safe_node
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next(&mut self) -> &mut BumpFrame {
|
||||
*self.generation.borrow_mut() += 1;
|
||||
|
||||
if *self.generation.borrow() % 2 == 0 {
|
||||
&mut self.frames[0]
|
||||
} else {
|
||||
&mut self.frames[1]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -191,10 +191,6 @@ impl VirtualDom {
|
|||
|
||||
// Ok(diff_machine.consume())
|
||||
}
|
||||
|
||||
pub fn base_scope(&self) -> &Scope {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================
|
||||
|
|
|
@ -32,7 +32,7 @@ mod traits {
|
|||
// Atoms, selectors, and their family variants are readable
|
||||
pub trait Readable<T: AtomValue>: Sized + Copy {
|
||||
fn use_read<'a, P: 'static>(self, ctx: Context<'a, P>) -> &'a T {
|
||||
hooks::use_read(ctx, self)
|
||||
hooks::use_read(&ctx, self)
|
||||
}
|
||||
|
||||
// This returns a future of the value
|
||||
|
@ -95,9 +95,9 @@ mod atoms {
|
|||
const EXAMPLE_ATOM: Atom<i32> = |_| 10;
|
||||
|
||||
// ensure that atoms are both read and write
|
||||
let _ = use_read(ctx, &EXAMPLE_ATOM);
|
||||
let _ = use_read_write(ctx, &EXAMPLE_ATOM);
|
||||
let _ = use_write(ctx, &EXAMPLE_ATOM);
|
||||
let _ = use_read(&ctx, &EXAMPLE_ATOM);
|
||||
let _ = use_read_write(&ctx, &EXAMPLE_ATOM);
|
||||
let _ = use_write(&ctx, &EXAMPLE_ATOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ mod atomfamily {
|
|||
|
||||
fn test(ctx: Context<()>) {
|
||||
let title = Titles.select(&10).use_read(ctx);
|
||||
let t2 = use_read(ctx, &Titles.select(&10));
|
||||
let t2 = use_read(&ctx, &Titles.select(&10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +368,7 @@ mod root {
|
|||
|
||||
mod hooks {
|
||||
use super::*;
|
||||
use dioxus_core::{hooks::use_ref, prelude::Context};
|
||||
use dioxus_core::{hooks::use_ref, prelude::Context, scope::Scoped};
|
||||
|
||||
pub fn use_init_recoil_root<P>(ctx: Context<P>, cfg: impl Fn(())) {
|
||||
ctx.use_create_context(move || RefCell::new(RecoilRoot::new()))
|
||||
|
@ -381,12 +381,12 @@ mod hooks {
|
|||
///
|
||||
/// You can use this method to create controllers that perform much more complex actions than set/get
|
||||
/// However, be aware that "getting" values through this hook will not subscribe the component to any updates.
|
||||
pub fn use_recoil_api<'a, P>(ctx: Context<'a, P>) -> &Rc<RecoilContext> {
|
||||
pub fn use_recoil_api<'a>(ctx: &impl Scoped<'a>) -> &'a Rc<RecoilContext> {
|
||||
ctx.use_context::<RecoilContext>()
|
||||
}
|
||||
|
||||
pub fn use_write<'a, T: AtomValue, P>(
|
||||
ctx: Context<'a, P>,
|
||||
pub fn use_write<'a, T: AtomValue>(
|
||||
ctx: &impl Scoped<'a>,
|
||||
// todo: this shouldn't need to be static
|
||||
writable: impl Writable<T>,
|
||||
) -> &'a Rc<dyn Fn(T)> {
|
||||
|
@ -412,10 +412,10 @@ mod hooks {
|
|||
/// Read the atom and get the Rc directly to the Atom's slot
|
||||
/// This is useful if you need the memoized Atom value. However, Rc<T> is not as easy to
|
||||
/// work with as
|
||||
pub fn use_read_raw<'a, T: AtomValue, P: 'static>(
|
||||
ctx: Context<'a, P>,
|
||||
pub fn use_read_raw<'a, T: AtomValue>(
|
||||
ctx: &impl Scoped<'a>,
|
||||
readable: impl Readable<T>,
|
||||
) -> &Rc<T> {
|
||||
) -> &'a Rc<T> {
|
||||
struct ReadHook<T> {
|
||||
value: Rc<T>,
|
||||
consumer_id: u32,
|
||||
|
@ -449,10 +449,7 @@ mod hooks {
|
|||
}
|
||||
|
||||
///
|
||||
pub fn use_read<'a, T: AtomValue, P: 'static>(
|
||||
ctx: Context<'a, P>,
|
||||
readable: impl Readable<T>,
|
||||
) -> &'a T {
|
||||
pub fn use_read<'a, T: AtomValue>(ctx: &impl Scoped<'a>, readable: impl Readable<T>) -> &'a T {
|
||||
use_read_raw(ctx, readable).as_ref()
|
||||
}
|
||||
|
||||
|
@ -471,8 +468,8 @@ mod hooks {
|
|||
/// // equivalent to:
|
||||
/// let (title, set_title) = (use_read(ctx, &Title), use_write(ctx, &Title));
|
||||
/// ```
|
||||
pub fn use_read_write<'a, T: AtomValue + 'static, P: 'static>(
|
||||
ctx: Context<'a, P>,
|
||||
pub fn use_read_write<'a, T: AtomValue + 'static>(
|
||||
ctx: &impl Scoped<'a>,
|
||||
writable: impl Writable<T>,
|
||||
) -> (&'a T, &'a Rc<dyn Fn(T)>) {
|
||||
(use_read(ctx, writable), use_write(ctx, writable))
|
||||
|
|
Loading…
Reference in a new issue