2021-12-13 05:16:05 +00:00
|
|
|
use dioxus_core::{AnyContext, Scope, ScopeId};
|
2021-10-11 19:35:20 +00:00
|
|
|
use std::{
|
|
|
|
cell::{Cell, Ref, RefCell, RefMut},
|
|
|
|
collections::HashSet,
|
|
|
|
rc::Rc,
|
|
|
|
};
|
|
|
|
|
|
|
|
type ProvidedState<T> = RefCell<ProvidedStateInner<T>>;
|
|
|
|
|
|
|
|
// Tracks all the subscribers to a shared State
|
2021-12-10 02:19:31 +00:00
|
|
|
pub struct ProvidedStateInner<T> {
|
2021-10-11 19:35:20 +00:00
|
|
|
value: Rc<RefCell<T>>,
|
|
|
|
notify_any: Rc<dyn Fn(ScopeId)>,
|
|
|
|
consumers: HashSet<ScopeId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> ProvidedStateInner<T> {
|
|
|
|
pub(crate) fn notify_consumers(&mut self) {
|
|
|
|
for consumer in self.consumers.iter() {
|
|
|
|
(self.notify_any)(*consumer);
|
|
|
|
}
|
|
|
|
}
|
2021-12-10 02:19:31 +00:00
|
|
|
|
|
|
|
pub fn write(&self) -> RefMut<T> {
|
|
|
|
self.value.borrow_mut()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read(&self) -> Ref<T> {
|
|
|
|
self.value.borrow()
|
|
|
|
}
|
2021-10-11 19:35:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// This hook provides some relatively light ergonomics around shared state.
|
|
|
|
///
|
|
|
|
/// It is not a substitute for a proper state management system, but it is capable enough to provide use_state - type
|
|
|
|
/// ergonimics in a pinch, with zero cost.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ## Provider
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// ## Consumer
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// # How it works
|
|
|
|
///
|
|
|
|
/// Any time a component calls `write`, every consumer of the state will be notified - excluding the provider.
|
|
|
|
///
|
|
|
|
/// Right now, there is not a distinction between read-only and write-only, so every consumer will be notified.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
///
|
2021-12-13 05:14:47 +00:00
|
|
|
pub fn use_shared_state<'a, T: 'static>(cx: &dyn AnyContext<'a>) -> Option<UseSharedState<'a, T>> {
|
|
|
|
let cx = cx.get_scope();
|
2021-10-11 19:35:20 +00:00
|
|
|
cx.use_hook(
|
|
|
|
|_| {
|
|
|
|
let scope_id = cx.scope_id();
|
|
|
|
let root = cx.consume_state::<ProvidedState<T>>();
|
|
|
|
|
|
|
|
if let Some(root) = root.as_ref() {
|
|
|
|
root.borrow_mut().consumers.insert(scope_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
let value = root.as_ref().map(|f| f.borrow().value.clone());
|
|
|
|
SharedStateInner {
|
|
|
|
root,
|
|
|
|
value,
|
|
|
|
scope_id,
|
|
|
|
needs_notification: Cell::new(false),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|f| {
|
|
|
|
//
|
|
|
|
f.needs_notification.set(false);
|
|
|
|
match (&f.value, &f.root) {
|
|
|
|
(Some(value), Some(root)) => Some(UseSharedState {
|
|
|
|
cx,
|
|
|
|
value,
|
|
|
|
root,
|
|
|
|
needs_notification: &f.needs_notification,
|
|
|
|
}),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SharedStateInner<T: 'static> {
|
|
|
|
root: Option<Rc<ProvidedState<T>>>,
|
|
|
|
value: Option<Rc<RefCell<T>>>,
|
|
|
|
scope_id: ScopeId,
|
|
|
|
needs_notification: Cell<bool>,
|
|
|
|
}
|
2021-11-07 03:11:17 +00:00
|
|
|
impl<T> Drop for SharedStateInner<T> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
// we need to unsubscribe when our component is unounted
|
|
|
|
if let Some(root) = &self.root {
|
|
|
|
let mut root = root.borrow_mut();
|
|
|
|
root.consumers.remove(&self.scope_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-10-11 19:35:20 +00:00
|
|
|
|
|
|
|
pub struct UseSharedState<'a, T: 'static> {
|
2021-12-13 05:14:47 +00:00
|
|
|
pub(crate) cx: &'a Scope,
|
2021-10-11 19:35:20 +00:00
|
|
|
pub(crate) value: &'a Rc<RefCell<T>>,
|
|
|
|
pub(crate) root: &'a Rc<RefCell<ProvidedStateInner<T>>>,
|
|
|
|
pub(crate) needs_notification: &'a Cell<bool>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T: 'static> UseSharedState<'a, T> {
|
|
|
|
pub fn read(&self) -> Ref<'_, T> {
|
|
|
|
self.value.borrow()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn notify_consumers(self) {
|
2021-12-10 02:19:31 +00:00
|
|
|
if !self.needs_notification.get() {
|
|
|
|
self.root.borrow_mut().notify_consumers();
|
|
|
|
self.needs_notification.set(true);
|
|
|
|
}
|
2021-10-11 19:35:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read_write(&self) -> (Ref<'_, T>, &Self) {
|
|
|
|
(self.read(), self)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Calling "write" will force the component to re-render
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// TODO: We prevent unncessary notifications only in the hook, but we should figure out some more global lock
|
2021-11-03 19:13:50 +00:00
|
|
|
pub fn write(&self) -> RefMut<'_, T> {
|
2021-10-11 19:35:20 +00:00
|
|
|
self.cx.needs_update();
|
|
|
|
self.notify_consumers();
|
|
|
|
self.value.borrow_mut()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Allows the ability to write the value without forcing a re-render
|
|
|
|
pub fn write_silent(&self) -> RefMut<'_, T> {
|
|
|
|
self.value.borrow_mut()
|
|
|
|
}
|
2021-12-10 02:19:31 +00:00
|
|
|
|
|
|
|
pub fn inner(&self) -> Rc<RefCell<ProvidedStateInner<T>>> {
|
|
|
|
self.root.clone()
|
|
|
|
}
|
2021-10-11 19:35:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Copy for UseSharedState<'_, T> {}
|
|
|
|
impl<'a, T> Clone for UseSharedState<'a, T>
|
|
|
|
where
|
|
|
|
T: 'static,
|
|
|
|
{
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
UseSharedState {
|
|
|
|
cx: self.cx,
|
|
|
|
value: self.value,
|
|
|
|
root: self.root,
|
|
|
|
needs_notification: self.needs_notification,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Provide some state for components down the hierarchy to consume without having to drill props.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
///
|
|
|
|
///
|
|
|
|
///
|
|
|
|
///
|
|
|
|
///
|
2021-12-13 05:14:47 +00:00
|
|
|
pub fn use_provide_state<'a, T: 'static>(cx: &dyn AnyContext<'a>, f: impl FnOnce() -> T) {
|
|
|
|
let cx = cx.get_scope();
|
2021-10-11 19:35:20 +00:00
|
|
|
cx.use_hook(
|
|
|
|
|_| {
|
2021-10-11 22:40:00 +00:00
|
|
|
let state: ProvidedState<T> = RefCell::new(ProvidedStateInner {
|
2021-10-11 19:35:20 +00:00
|
|
|
value: Rc::new(RefCell::new(f())),
|
|
|
|
notify_any: cx.schedule_update_any(),
|
|
|
|
consumers: HashSet::new(),
|
2021-10-11 22:40:00 +00:00
|
|
|
});
|
|
|
|
cx.provide_state(state)
|
2021-10-11 19:35:20 +00:00
|
|
|
},
|
2021-12-01 03:48:05 +00:00
|
|
|
|_inner| {},
|
2021-10-11 19:35:20 +00:00
|
|
|
)
|
|
|
|
}
|