mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-01-11 20:28:55 +00:00
137 lines
3.2 KiB
Rust
137 lines
3.2 KiB
Rust
|
//! When building complex components, it's occasionally useful to dip into a pure MVC pattern instead of the
|
||
|
//! React hooks pattern. Hooks are useful to abstract over some reusable logic, but many models are not reusable
|
||
|
//! in the same way that hooks are.
|
||
|
//!
|
||
|
//! In these cases, we provide `use_model` - a convenient way of abstracting over some state and async functions.
|
||
|
|
||
|
use dioxus_core::prelude::Context;
|
||
|
use futures::Future;
|
||
|
use std::{
|
||
|
cell::{Cell, Ref, RefCell, RefMut},
|
||
|
marker::PhantomData,
|
||
|
pin::Pin,
|
||
|
rc::Rc,
|
||
|
};
|
||
|
|
||
|
pub fn use_model<T: 'static>(cx: Context, f: impl FnOnce() -> T) -> UseModel<T> {
|
||
|
cx.use_hook(
|
||
|
|_| UseModelInner {
|
||
|
update_scheduled: Cell::new(false),
|
||
|
update_callback: cx.schedule_update(),
|
||
|
value: RefCell::new(f()),
|
||
|
// tasks: RefCell::new(Vec::new()),
|
||
|
},
|
||
|
|inner| {
|
||
|
inner.update_scheduled.set(false);
|
||
|
UseModel { inner }
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
|
||
|
pub struct UseModel<'a, T> {
|
||
|
inner: &'a UseModelInner<T>,
|
||
|
}
|
||
|
|
||
|
struct UseModelInner<T> {
|
||
|
update_scheduled: Cell<bool>,
|
||
|
update_callback: Rc<dyn Fn()>,
|
||
|
value: RefCell<T>,
|
||
|
// tasks: RefCell<Vec<ModelTask>>,
|
||
|
}
|
||
|
|
||
|
type ModelTask = Pin<Box<dyn Future<Output = ()> + 'static>>;
|
||
|
|
||
|
impl<'a, T: 'static> UseModel<'a, T> {
|
||
|
pub fn read(&self) -> Ref<'_, T> {
|
||
|
self.inner.value.borrow()
|
||
|
}
|
||
|
pub fn write(&self) -> RefMut<'_, T> {
|
||
|
self.needs_update();
|
||
|
self.inner.value.borrow_mut()
|
||
|
}
|
||
|
/// Allows the ability to write the value without forcing a re-render
|
||
|
pub fn write_silent(&self) -> RefMut<'_, T> {
|
||
|
self.inner.value.borrow_mut()
|
||
|
}
|
||
|
|
||
|
pub fn needs_update(&self) {
|
||
|
if !self.inner.update_scheduled.get() {
|
||
|
self.inner.update_scheduled.set(true);
|
||
|
(self.inner.update_callback)();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn set(&self, new: T) {
|
||
|
*self.inner.value.borrow_mut() = new;
|
||
|
self.needs_update();
|
||
|
}
|
||
|
|
||
|
pub fn read_write(&self) -> (Ref<'_, T>, &Self) {
|
||
|
(self.read(), self)
|
||
|
}
|
||
|
|
||
|
pub fn start(&self, f: impl FnOnce() -> ModelTask) {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// keep a coroutine going
|
||
|
pub fn use_model_coroutine<T, F: Future<Output = ()> + 'static>(
|
||
|
cx: Context,
|
||
|
model: UseModel<T>,
|
||
|
f: impl FnOnce(AppModels) -> F,
|
||
|
) -> UseModelCoroutine {
|
||
|
cx.use_hook(
|
||
|
|_| {
|
||
|
//
|
||
|
UseModelTaskInner {
|
||
|
task: Default::default(),
|
||
|
}
|
||
|
},
|
||
|
|inner| {
|
||
|
if let Some(task) = inner.task.get_mut() {
|
||
|
cx.push_task(|| task);
|
||
|
}
|
||
|
//
|
||
|
todo!()
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
|
||
|
pub struct UseModelCoroutine {}
|
||
|
|
||
|
struct UseModelTaskInner {
|
||
|
task: RefCell<Option<ModelTask>>,
|
||
|
}
|
||
|
|
||
|
impl UseModelCoroutine {
|
||
|
pub fn start(&self) {}
|
||
|
}
|
||
|
|
||
|
pub struct ModelAsync<T> {
|
||
|
_p: PhantomData<T>,
|
||
|
}
|
||
|
impl<T> ModelAsync<T> {
|
||
|
pub fn write(&self) -> RefMut<'_, T> {
|
||
|
todo!()
|
||
|
}
|
||
|
pub fn read(&self) -> Ref<'_, T> {
|
||
|
todo!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct AppModels {}
|
||
|
|
||
|
impl AppModels {
|
||
|
pub fn get<T: 'static>(&self) -> ModelAsync<T> {
|
||
|
unimplemented!()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<T> Copy for UseModel<'_, T> {}
|
||
|
impl<'a, T> Clone for UseModel<'a, T> {
|
||
|
fn clone(&self) -> Self {
|
||
|
Self { inner: self.inner }
|
||
|
}
|
||
|
}
|