mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 14:44:12 +00:00
feat: refactor out the hooks implementation
This commit is contained in:
parent
eb82051000
commit
1cc1679a6b
8 changed files with 163 additions and 37 deletions
4
packages/core/.vscode/settings.json
vendored
4
packages/core/.vscode/settings.json
vendored
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.inlayHints.enable": true
|
||||
}
|
||||
"rust-analyzer.inlayHints.enable": false
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ futures = "0.3.15"
|
|||
|
||||
|
||||
async-std = { version="1.9.0", features=["attributes"] }
|
||||
appendlist = "1.4.0"
|
||||
|
||||
[features]
|
||||
default = ["serialize"]
|
||||
|
|
21
packages/core/examples/async.rs
Normal file
21
packages/core/examples/async.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use std::pin::Pin;
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
use futures::Future;
|
||||
|
||||
fn main() {}
|
||||
|
||||
const App: FC<()> = |cx| {
|
||||
let mut fut = cx.use_hook(
|
||||
|| {
|
||||
//
|
||||
Box::pin(async { loop {} }) as Pin<Box<dyn Future<Output = ()>>>
|
||||
},
|
||||
|f| f,
|
||||
|_| {},
|
||||
);
|
||||
|
||||
cx.submit_task(fut);
|
||||
|
||||
todo!()
|
||||
};
|
65
packages/core/src/hooklist.rs
Normal file
65
packages/core/src/hooklist.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use std::{
|
||||
any::Any,
|
||||
cell::{Cell, UnsafeCell},
|
||||
};
|
||||
|
||||
pub struct HookList {
|
||||
vals: appendlist::AppendList<InnerHook<Box<dyn Any>>>,
|
||||
idx: Cell<usize>,
|
||||
}
|
||||
|
||||
impl Default for HookList {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
vals: Default::default(),
|
||||
idx: Cell::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InnerHook<T> {
|
||||
cell: UnsafeCell<T>,
|
||||
}
|
||||
impl<T> InnerHook<T> {
|
||||
fn new(new: T) -> Self {
|
||||
Self {
|
||||
cell: UnsafeCell::new(new),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HookList {
|
||||
pub(crate) fn push<T: 'static>(&self, new: T) {
|
||||
self.vals.push(InnerHook::new(Box::new(new)))
|
||||
}
|
||||
|
||||
pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
|
||||
self.vals.get(self.idx.get()).and_then(|inn| {
|
||||
self.idx.set(self.idx.get() + 1);
|
||||
let mut raw_box = unsafe { &mut *inn.cell.get() };
|
||||
raw_box.downcast_mut::<T>()
|
||||
})
|
||||
}
|
||||
|
||||
/// This resets the internal iterator count
|
||||
/// It's okay that we've given out each hook, but now we have the opportunity to give it out again
|
||||
/// Therefore, resetting is cosudered unsafe
|
||||
pub(crate) unsafe fn reset(&mut self) {
|
||||
self.idx.set(0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.vals.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn cur_idx(&self) -> usize {
|
||||
self.idx.get()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_finished(&self) -> bool {
|
||||
self.idx.get() == self.vals.len()
|
||||
}
|
||||
}
|
|
@ -13,10 +13,11 @@ pub mod serialize;
|
|||
|
||||
pub mod arena;
|
||||
pub mod component;
|
||||
pub mod hooklist;
|
||||
pub mod styles;
|
||||
pub mod tasks;
|
||||
pub mod util; // Logic for extending FC
|
||||
|
||||
// pub mod debug_renderer;
|
||||
// pub mod debug_renderer;
|
||||
pub mod diff;
|
||||
|
||||
pub mod error; // Error type we expose to the renderers
|
||||
|
|
|
@ -427,6 +427,7 @@ fn create_closure<'a, P: 'a>(
|
|||
let cx: Context<P> = Context {
|
||||
props: safe_props,
|
||||
scope: scp,
|
||||
tasks: todo!(),
|
||||
};
|
||||
|
||||
let g = component(cx);
|
||||
|
|
38
packages/core/src/tasks.rs
Normal file
38
packages/core/src/tasks.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
//! The TaskQueue serves as a centralized async store for all tasks in Dioxus.
|
||||
//! When a component renders, it may submit an async task to the queue.
|
||||
//!
|
||||
//! Then the task complete, it is emitted from the virtual dom in the event loop, which is then fed back into the virtualdom
|
||||
//! as an event trigger.
|
||||
//!
|
||||
//! When a component is scheduled to re-render, the awaing task must be dumped from the queue.
|
||||
//!
|
||||
//! This is all pretty unsafe stuff.
|
||||
//! The major invariant here is that tasks that enter the queue may be invalidated during transitions.
|
||||
|
||||
use std::pin::Pin;
|
||||
|
||||
use futures::{Future, Stream, StreamExt};
|
||||
use slotmap::{DefaultKey, SlotMap};
|
||||
|
||||
use crate::events::EventTrigger;
|
||||
|
||||
pub struct TaskQueue {
|
||||
slots: SlotMap<DefaultKey, Task>,
|
||||
}
|
||||
|
||||
impl TaskQueue {
|
||||
unsafe fn push_task(&mut self, task: Task) -> TaskHandle {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn next(&mut self) -> EventTrigger {
|
||||
for (key, task) in self.slots.iter_mut() {
|
||||
let ptr = task.0;
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct Task(*mut Pin<Box<dyn Future<Output = ()>>>);
|
||||
|
||||
struct TaskHandle {}
|
|
@ -19,11 +19,14 @@
|
|||
//! This module includes just the barebones for a complete VirtualDOM API.
|
||||
//! Additional functionality is defined in the respective files.
|
||||
|
||||
use crate::hooklist::HookList;
|
||||
use crate::{arena::ScopeArena, innerlude::*};
|
||||
use appendlist::AppendList;
|
||||
use bumpalo::Bump;
|
||||
use futures::FutureExt;
|
||||
use slotmap::DefaultKey;
|
||||
use slotmap::SlotMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell},
|
||||
|
@ -159,7 +162,11 @@ impl VirtualDom {
|
|||
// the lifetime of this closure is just as long as the lifetime on the scope reference
|
||||
// this closure moves root props (which is static) into this closure
|
||||
let props = unsafe { &*(&root_props as *const _) };
|
||||
root(Context { props, scope })
|
||||
root(Context {
|
||||
props,
|
||||
scope,
|
||||
tasks: todo!(),
|
||||
})
|
||||
});
|
||||
|
||||
// Create a weak reference to the OpaqueComponent for the root scope to use as its render function
|
||||
|
@ -379,8 +386,6 @@ pub struct Scope {
|
|||
|
||||
pub caller: Weak<OpaqueComponent>,
|
||||
|
||||
pub hookidx: Cell<usize>,
|
||||
|
||||
// ==========================
|
||||
// slightly unsafe stuff
|
||||
// ==========================
|
||||
|
@ -391,8 +396,8 @@ pub struct Scope {
|
|||
// 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>>,
|
||||
|
||||
hooks: HookList,
|
||||
// hooks: RefCell<Vec<Hook>>,
|
||||
pub(crate) listener_idx: Cell<usize>,
|
||||
|
||||
// Unsafety:
|
||||
|
@ -410,7 +415,7 @@ pub struct Scope {
|
|||
}
|
||||
|
||||
// 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 Hook = Box<dyn std::any::Any>;
|
||||
type EventChannel = Rc<dyn Fn()>;
|
||||
|
||||
impl Scope {
|
||||
|
@ -470,7 +475,6 @@ impl Scope {
|
|||
hooks: Default::default(),
|
||||
shared_contexts: Default::default(),
|
||||
listeners: Default::default(),
|
||||
hookidx: Default::default(),
|
||||
descendents: Default::default(),
|
||||
suspended_tasks: Default::default(),
|
||||
}
|
||||
|
@ -510,7 +514,7 @@ impl Scope {
|
|||
// Remove all the outdated listeners
|
||||
self.listeners.borrow_mut().clear();
|
||||
|
||||
self.hookidx.set(0);
|
||||
unsafe { self.hooks.reset() };
|
||||
self.listener_idx.set(0);
|
||||
|
||||
let caller = self
|
||||
|
@ -611,7 +615,13 @@ impl Scope {
|
|||
pub struct Context<'src, T> {
|
||||
pub props: &'src T,
|
||||
pub scope: &'src Scope,
|
||||
pub tasks: &'src AppendList<&'src mut DTask>,
|
||||
// pub task: &'src RefCell<Vec<&'src mut >>,
|
||||
}
|
||||
pub type DTask = Pin<Box<dyn Future<Output = ()>>>;
|
||||
// // pub task: &'src RefCell<Option<&'src mut Pin<Box<dyn Future<Output = ()>>>>>,
|
||||
// pub task: Option<()>, // pub task: &'src RefCell<Option<&'src mut Pin<Box<dyn Future<Output = ()>>>>>,
|
||||
// pub task: &'src RefCell<Option<&'src mut Pin<Box<dyn Future<Output = ()>>>>>
|
||||
|
||||
impl<'src, T> Copy for Context<'src, T> {}
|
||||
impl<'src, T> Clone for Context<'src, T> {
|
||||
|
@ -619,6 +629,7 @@ impl<'src, T> Clone for Context<'src, T> {
|
|||
Self {
|
||||
props: self.props,
|
||||
scope: self.scope,
|
||||
tasks: todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -712,29 +723,13 @@ impl<'src, P> Context<'src, P> {
|
|||
// TODO: add this to the "clean up" group for when the component is dropped
|
||||
_cleanup: impl FnOnce(InternalHookState),
|
||||
) -> Output {
|
||||
let scope = self.scope;
|
||||
|
||||
let idx = scope.hookidx.get();
|
||||
|
||||
// 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() {
|
||||
if self.scope.hooks.is_finished() {
|
||||
let new_state = initializer();
|
||||
hooks.push(Box::pin(new_state));
|
||||
self.scope.hooks.push(Box::new(new_state));
|
||||
}
|
||||
|
||||
scope.hookidx.set(idx + 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(
|
||||
let state = self.scope.hooks.next::<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.
|
||||
|
@ -744,8 +739,7 @@ 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 _) })
|
||||
runner(state)
|
||||
}
|
||||
|
||||
/// This hook enables the ability to expose state to children further down the VirtualDOM Tree.
|
||||
|
@ -882,16 +876,21 @@ Any function prefixed with "use" should not be called conditionally.
|
|||
///
|
||||
pub fn submit_task(
|
||||
&self,
|
||||
task: &'src mut Pin<Box<dyn Future<Output = ()> + 'static>>,
|
||||
mut task: &'src mut Pin<Box<dyn Future<Output = ()> + 'static>>,
|
||||
) -> TaskHandle {
|
||||
self.tasks.push(task);
|
||||
// let mut g = self.task.borrow_mut();
|
||||
// *g = Some(task);
|
||||
// the pointer to the task is stable - we guarantee stability of all &'src references
|
||||
let task_ptr = task as *mut _;
|
||||
// let task_ptr = task as *mut _;
|
||||
|
||||
TaskHandle {}
|
||||
TaskHandle { _p: PhantomData {} }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TaskHandle {}
|
||||
pub struct TaskHandle<'src> {
|
||||
_p: PhantomData<&'src ()>,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct SuspendedContext {}
|
||||
|
||||
|
|
Loading…
Reference in a new issue