Merge branch 'master' into maybe-sync-signal
This commit is contained in:
1 parent
c1bfe9514f
commit
70c3abb8df
118 files changed
+2206
-412
No files matched your search
@@ -60,6 +60,9 @@ pub mod computed;
|
||||
mod use_on_destroy;
|
||||
pub use use_on_destroy::*;
|
||||
|
||||
mod use_const;
|
||||
pub use use_const::*;
|
||||
|
||||
mod use_context;
|
||||
pub use use_context::*;
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
/// Store constant state between component renders.
|
||||
///
|
||||
/// UseConst allows you to store state that is initialized once and then remains constant across renders.
|
||||
/// You can only get an immutable reference after initalization.
|
||||
/// This can be useful for values that don't need to update reactively, thus can be memoized easily
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// struct ComplexData(i32);
|
||||
///
|
||||
/// fn Component(cx: Scope) -> Element {
|
||||
/// let id = use_const(cx, || ComplexData(100));
|
||||
///
|
||||
/// cx.render(rsx! {
|
||||
/// div { "{id.0}" }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn use_const<T: 'static>(
|
||||
cx: &ScopeState,
|
||||
initial_state_fn: impl FnOnce() -> T,
|
||||
) -> &UseConst<T> {
|
||||
cx.use_hook(|| UseConst {
|
||||
value: Rc::new(initial_state_fn()),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UseConst<T> {
|
||||
value: Rc<T>,
|
||||
}
|
||||
|
||||
impl<T> PartialEq for UseConst<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Rc::ptr_eq(&self.value, &other.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: core::fmt::Display> core::fmt::Display for UseConst<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.value.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UseConst<T> {
|
||||
pub fn get_rc(&self) -> &Rc<T> {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for UseConst<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.value.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_const_makes_sense() {
|
||||
#[allow(unused)]
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let const_val = use_const(cx, || vec![0, 1, 2, 3]);
|
||||
|
||||
assert!(const_val[0] == 0);
|
||||
|
||||
// const_val.remove(0); // Cannot Compile, cannot get mutable reference now
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
use dioxus_core::{ScopeState, TaskId};
|
||||
use std::{any::Any, cell::Cell, future::Future};
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
future::Future,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::UseFutureDep;
|
||||
|
||||
@@ -14,7 +19,7 @@ use crate::UseFutureDep;
|
||||
/// ## Arguments
|
||||
///
|
||||
/// - `dependencies`: a tuple of references to values that are `PartialEq` + `Clone`.
|
||||
/// - `future`: a closure that takes the `dependencies` as arguments and returns a `'static` future.
|
||||
/// - `future`: a closure that takes the `dependencies` as arguments and returns a `'static` future. That future may return nothing or a closure that will be executed when the dependencies change to clean up the effect.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
@@ -33,6 +38,16 @@ use crate::UseFutureDep;
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// // Only fetch the user data when the id changes.
|
||||
/// use_effect(cx, (id,), |(id,)| {
|
||||
/// to_owned![name];
|
||||
/// async move {
|
||||
/// let user = fetch_user(id).await;
|
||||
/// name.set(user.name);
|
||||
/// move || println!("Cleaning up from {}", id)
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// let name = name.get().clone().unwrap_or("Loading...".to_string());
|
||||
///
|
||||
/// render!(
|
||||
@@ -45,34 +60,80 @@ use crate::UseFutureDep;
|
||||
/// render!(Profile { id: 0 })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn use_effect<T, F, D>(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> F)
|
||||
pub fn use_effect<T, R, D>(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> R)
|
||||
where
|
||||
T: 'static,
|
||||
F: Future<Output = T> + 'static,
|
||||
D: UseFutureDep,
|
||||
R: UseEffectReturn<T>,
|
||||
{
|
||||
struct UseEffect {
|
||||
needs_regen: bool,
|
||||
task: Cell<Option<TaskId>>,
|
||||
dependencies: Vec<Box<dyn Any>>,
|
||||
cleanup: UseEffectCleanup,
|
||||
}
|
||||
|
||||
impl Drop for UseEffect {
|
||||
fn drop(&mut self) {
|
||||
if let Some(cleanup) = self.cleanup.borrow_mut().take() {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let state = cx.use_hook(move || UseEffect {
|
||||
needs_regen: true,
|
||||
task: Cell::new(None),
|
||||
dependencies: Vec::new(),
|
||||
cleanup: Rc::new(RefCell::new(None)),
|
||||
});
|
||||
|
||||
if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen {
|
||||
// Call the cleanup function if it exists
|
||||
if let Some(cleanup) = state.cleanup.borrow_mut().take() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
// We don't need regen anymore
|
||||
state.needs_regen = false;
|
||||
|
||||
// Create the new future
|
||||
let fut = future(dependencies.out());
|
||||
let return_value = future(dependencies.out());
|
||||
|
||||
state.task.set(Some(cx.push_future(async move {
|
||||
fut.await;
|
||||
})));
|
||||
if let Some(task) = return_value.apply(state.cleanup.clone(), cx) {
|
||||
state.task.set(Some(task));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type UseEffectCleanup = Rc<RefCell<Option<Box<dyn FnOnce()>>>>;
|
||||
|
||||
/// Something that can be returned from a `use_effect` hook.
|
||||
pub trait UseEffectReturn<T> {
|
||||
fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> Option<TaskId>;
|
||||
}
|
||||
|
||||
impl<T> UseEffectReturn<()> for T
|
||||
where
|
||||
T: Future<Output = ()> + 'static,
|
||||
{
|
||||
fn apply(self, _: UseEffectCleanup, cx: &ScopeState) -> Option<TaskId> {
|
||||
Some(cx.push_future(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct CleanupFutureMarker;
|
||||
impl<T, F> UseEffectReturn<CleanupFutureMarker> for T
|
||||
where
|
||||
T: Future<Output = F> + 'static,
|
||||
F: FnOnce() + 'static,
|
||||
{
|
||||
fn apply(self, oncleanup: UseEffectCleanup, cx: &ScopeState) -> Option<TaskId> {
|
||||
let task = cx.push_future(async move {
|
||||
let cleanup = self.await;
|
||||
*oncleanup.borrow_mut() = Some(Box::new(cleanup) as Box<dyn FnOnce()>);
|
||||
});
|
||||
Some(task)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,13 +31,14 @@ where
|
||||
|
||||
let state = cx.use_hook(move || UseFuture {
|
||||
update: cx.schedule_update(),
|
||||
needs_regen: Cell::new(true),
|
||||
needs_regen: Rc::new(Cell::new(true)),
|
||||
state: val.clone(),
|
||||
task: Default::default(),
|
||||
dependencies: Vec::new(),
|
||||
});
|
||||
|
||||
if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen.get() {
|
||||
let state_dependencies = cx.use_hook(Vec::new);
|
||||
|
||||
if dependencies.clone().apply(state_dependencies) || state.needs_regen.get() {
|
||||
// kill the old one, if it exists
|
||||
if let Some(task) = state.task.take() {
|
||||
cx.remove_future(task);
|
||||
@@ -69,11 +70,11 @@ pub enum FutureState<'a, T> {
|
||||
Regenerating(&'a T), // the old value
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UseFuture<T: 'static> {
|
||||
update: Arc<dyn Fn()>,
|
||||
needs_regen: Cell<bool>,
|
||||
needs_regen: Rc<Cell<bool>>,
|
||||
task: Rc<Cell<Option<TaskId>>>,
|
||||
dependencies: Vec<Box<dyn Any>>,
|
||||
state: UseState<Option<T>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ macro_rules! debug_location {
|
||||
}
|
||||
|
||||
pub mod error {
|
||||
#[cfg(debug_assertions)]
|
||||
fn locations_display(locations: &[&'static std::panic::Location<'static>]) -> String {
|
||||
locations
|
||||
.iter()
|
||||
|
||||
Reference in new issue
Block a user