Move to a generic GlobalLazy<T> (#2851)

* Expose a generic lazy type

* Switch to generic lazy globals

* simplify global lazy context a bit

* rename LazyGlobal to Global

* use Memo::global in more examples

* Fix soundness issue with deref_impl. It relies on the size of self, so it cannot be safe

* add a comment about safety

* Make clippy happy

* fix formatting

* Restore changes to signal impl

* Add helper methods for global signal and global memo to make getting the inner value easier
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-08-16 17:57:41 -05:00
1 parent 668d5415c8
commit b47a6cf83e
13 files changed
+281 -289

No files matched your search

+15 -82
View File
@@ -1,93 +1,26 @@
use crate::{read::Readable, Memo, ReadableRef};
use crate::{read_impls, GlobalKey};
use dioxus_core::prelude::ScopeId;
use generational_box::{BorrowResult, UnsyncStorage};
use std::ops::Deref;
use super::{Global, InitializeFromFunction};
use crate::read::Readable;
use crate::read_impls;
use crate::Memo;
use crate::Signal;
use super::get_global_context;
/// A signal that can be accessed from anywhere in the application and created in a static
pub struct GlobalMemo<T: 'static> {
selector: fn() -> T,
key: GlobalKey<'static>,
impl<T: PartialEq> InitializeFromFunction<T> for Memo<T> {
fn initialize_from_function(f: fn() -> T) -> Self {
Memo::new(f)
}
}
/// A memo that can be accessed from anywhere in the application and created in a static
pub type GlobalMemo<T> = Global<Memo<T>, T>;
impl<T: PartialEq + 'static> GlobalMemo<T> {
#[track_caller]
/// Create a new global signal
pub const fn new(selector: fn() -> T) -> GlobalMemo<T>
where
T: PartialEq,
{
let key = std::panic::Location::caller();
GlobalMemo {
selector,
key: GlobalKey::new(key),
}
}
/// Get the key for this global
pub fn key(&self) -> GlobalKey<'static> {
self.key.clone()
}
/// Get the signal that backs this global.
pub fn memo(&self) -> Memo<T> {
let key = self.key();
let context = get_global_context();
let read = context.signal.borrow();
match read.get(&key) {
Some(signal) => *signal.downcast_ref::<Memo<T>>().unwrap(),
None => {
drop(read);
// Constructors are always run in the root scope
let signal = ScopeId::ROOT.in_runtime(|| Signal::memo(self.selector));
context.signal.borrow_mut().insert(key, Box::new(signal));
signal
}
}
}
/// Get the scope the signal was created in.
pub fn origin_scope(&self) -> ScopeId {
ScopeId::ROOT
}
/// Get the generational id of the signal.
pub fn id(&self) -> generational_box::GenerationalBoxId {
self.memo().id()
}
}
impl<T: PartialEq + 'static> Readable for GlobalMemo<T> {
type Target = T;
type Storage = UnsyncStorage;
#[track_caller]
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
self.memo().try_read_unchecked()
self.resolve().id()
}
#[track_caller]
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
self.memo().try_peek_unchecked()
}
}
/// Allow calling a signal with memo() syntax
///
/// Currently only limited to copy types, though could probably specialize for string/arc/rc
impl<T: PartialEq + Clone + 'static> Deref for GlobalMemo<T> {
type Target = dyn Fn() -> T;
fn deref(&self) -> &Self::Target {
Readable::deref_impl(self)
/// Resolve the global memo. This will try to get the existing value from the current virtual dom, and if it doesn't exist, it will create a new one.
pub fn memo(&self) -> Memo<T> {
self.resolve()
}
}