Merge branch 'master' into feature/use-shared-state-better-diagnostics
This commit is contained in:
324 files changed
+13509
-6646
No files matched your search
@@ -9,17 +9,33 @@ use crate::UseFutureDep;
|
||||
/// If a future is pending when the dependencies change, the previous future
|
||||
/// will be allowed to continue
|
||||
///
|
||||
/// - dependencies: a tuple of references to values that are PartialEq + Clone
|
||||
/// - dependencies: a tuple of references to values that are `PartialEq` + `Clone`
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust, ignore
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// #[inline_props]
|
||||
/// fn app(cx: Scope, name: &str) -> Element {
|
||||
/// use_effect(cx, (name,), |(name,)| async move {
|
||||
/// set_title(name);
|
||||
/// }))
|
||||
/// fn Profile(cx: Scope, id: usize) -> Element {
|
||||
/// let name = use_state(cx, || None);
|
||||
///
|
||||
/// // 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);
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// let name = name.get().clone().unwrap_or("Loading...".to_string());
|
||||
///
|
||||
/// render!(
|
||||
/// p { "{name}" }
|
||||
/// )
|
||||
/// }
|
||||
///
|
||||
/// fn app(cx: Scope) -> Element {
|
||||
/// render!(Profile { id: 0 })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn use_effect<T, F, D>(cx: &ScopeState, dependencies: D, future: impl FnOnce(D::Out) -> F)
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
#![allow(missing_docs)]
|
||||
use dioxus_core::{ScopeState, TaskId};
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
future::{Future, IntoFuture},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{any::Any, cell::Cell, future::Future, rc::Rc, sync::Arc};
|
||||
|
||||
use crate::{use_state, UseState};
|
||||
|
||||
/// A future that resolves to a value.
|
||||
///
|
||||
@@ -31,43 +27,30 @@ where
|
||||
F: Future<Output = T> + 'static,
|
||||
D: UseFutureDep,
|
||||
{
|
||||
let val = use_state(cx, || None);
|
||||
|
||||
let state = cx.use_hook(move || UseFuture {
|
||||
update: cx.schedule_update(),
|
||||
needs_regen: Cell::new(true),
|
||||
values: Default::default(),
|
||||
task: Cell::new(None),
|
||||
state: val.clone(),
|
||||
task: Default::default(),
|
||||
dependencies: Vec::new(),
|
||||
waker: Default::default(),
|
||||
});
|
||||
|
||||
*state.waker.borrow_mut() = None;
|
||||
|
||||
if dependencies.clone().apply(&mut state.dependencies) || state.needs_regen.get() {
|
||||
// We don't need regen anymore
|
||||
state.needs_regen.set(false);
|
||||
// kill the old one, if it exists
|
||||
if let Some(task) = state.task.take() {
|
||||
cx.remove_future(task);
|
||||
}
|
||||
|
||||
// Create the new future
|
||||
let fut = future(dependencies.out());
|
||||
|
||||
// Clone in our cells
|
||||
let values = state.values.clone();
|
||||
let schedule_update = state.update.clone();
|
||||
let waker = state.waker.clone();
|
||||
|
||||
// Cancel the current future
|
||||
if let Some(current) = state.task.take() {
|
||||
cx.remove_future(current);
|
||||
}
|
||||
let val = val.clone();
|
||||
let task = state.task.clone();
|
||||
|
||||
state.task.set(Some(cx.push_future(async move {
|
||||
let res = fut.await;
|
||||
values.borrow_mut().push(Box::leak(Box::new(res)));
|
||||
|
||||
// if there's a waker, we dont re-render the component. Instead we just progress that future
|
||||
match waker.borrow().as_ref() {
|
||||
Some(waker) => waker.wake_by_ref(),
|
||||
None => schedule_update(),
|
||||
}
|
||||
val.set(Some(fut.await));
|
||||
task.take();
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -80,21 +63,12 @@ pub enum FutureState<'a, T> {
|
||||
Regenerating(&'a T), // the old value
|
||||
}
|
||||
|
||||
pub struct UseFuture<T> {
|
||||
pub struct UseFuture<T: 'static> {
|
||||
update: Arc<dyn Fn()>,
|
||||
needs_regen: Cell<bool>,
|
||||
task: Cell<Option<TaskId>>,
|
||||
task: Rc<Cell<Option<TaskId>>>,
|
||||
dependencies: Vec<Box<dyn Any>>,
|
||||
waker: Rc<RefCell<Option<std::task::Waker>>>,
|
||||
values: Rc<RefCell<Vec<*mut T>>>,
|
||||
}
|
||||
|
||||
impl<T> Drop for UseFuture<T> {
|
||||
fn drop(&mut self) {
|
||||
for value in self.values.take().into_iter() {
|
||||
drop(unsafe { Box::from_raw(value) })
|
||||
}
|
||||
}
|
||||
state: UseState<Option<T>>,
|
||||
}
|
||||
|
||||
pub enum UseFutureState<'a, T> {
|
||||
@@ -120,30 +94,16 @@ impl<T> UseFuture<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// clears the value in the future slot without starting the future over
|
||||
pub fn clear(&self) -> Option<T> {
|
||||
todo!()
|
||||
// (self.update)();
|
||||
// self.slot.replace(None)
|
||||
}
|
||||
|
||||
// Manually set the value in the future slot without starting the future over
|
||||
pub fn set(&self, _new_value: T) {
|
||||
// self.slot.set(Some(new_value));
|
||||
// self.needs_regen.set(true);
|
||||
// (self.update)();
|
||||
todo!()
|
||||
pub fn set(&self, new_value: T) {
|
||||
self.state.set(Some(new_value));
|
||||
}
|
||||
|
||||
/// Return any value, even old values if the future has not yet resolved.
|
||||
///
|
||||
/// If the future has never completed, the returned value will be `None`.
|
||||
pub fn value(&self) -> Option<&T> {
|
||||
self.values
|
||||
.borrow_mut()
|
||||
.last()
|
||||
.cloned()
|
||||
.map(|x| unsafe { &*x })
|
||||
self.state.current_val.as_ref().as_ref()
|
||||
}
|
||||
|
||||
/// Get the ID of the future in Dioxus' internal scheduler
|
||||
@@ -169,35 +129,6 @@ impl<T> UseFuture<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IntoFuture for &'a UseFuture<T> {
|
||||
type Output = &'a T;
|
||||
type IntoFuture = UseFutureAwait<'a, T>;
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
UseFutureAwait { hook: self }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UseFutureAwait<'a, T> {
|
||||
hook: &'a UseFuture<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> Future for UseFutureAwait<'a, T> {
|
||||
type Output = &'a T;
|
||||
|
||||
fn poll(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Self::Output> {
|
||||
match self.hook.values.borrow_mut().last().cloned() {
|
||||
Some(value) => std::task::Poll::Ready(unsafe { &*value }),
|
||||
None => {
|
||||
self.hook.waker.replace(Some(cx.waker().clone()));
|
||||
std::task::Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UseFutureDep: Sized + Clone {
|
||||
type Out;
|
||||
fn out(&self) -> Self::Out;
|
||||
@@ -343,10 +274,6 @@ mod tests {
|
||||
let blah = "asd";
|
||||
});
|
||||
|
||||
let g2 = a.await;
|
||||
|
||||
let g = fut.await;
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,27 @@ use dioxus_core::ScopeState;
|
||||
|
||||
use crate::UseFutureDep;
|
||||
|
||||
/// A hook that provides a callback that executes after the hooks have been applied
|
||||
/// A hook that provides a callback that executes if the dependencies change.
|
||||
/// This is useful to avoid running computation-expensive calculations even when the data doesn't change.
|
||||
///
|
||||
/// Whenever the hooks dependencies change, the callback will be re-evaluated.
|
||||
///
|
||||
/// - dependencies: a tuple of references to values that are PartialEq + Clone
|
||||
/// - dependencies: a tuple of references to values that are `PartialEq` + `Clone`
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
///
|
||||
/// #[inline_props]
|
||||
/// fn app(cx: Scope, name: &str) -> Element {
|
||||
/// use_memo(cx, (name,), |(name,)| {
|
||||
/// expensive_computation(name);
|
||||
/// }))
|
||||
/// fn Calculator(cx: Scope, number: usize) -> Element {
|
||||
/// let bigger_number = use_memo(cx, (number,), |(number,)| {
|
||||
/// // This will only be calculated when `number` has changed.
|
||||
/// number * 100
|
||||
/// });
|
||||
/// render!(
|
||||
/// p { "{bigger_number}" }
|
||||
/// )
|
||||
/// }
|
||||
/// fn app(cx: Scope) -> Element {
|
||||
/// render!(Calculator { number: 0 })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn use_memo<T, D>(cx: &ScopeState, dependencies: D, callback: impl FnOnce(D::Out) -> T) -> &T
|
||||
|
||||
@@ -16,7 +16,7 @@ use std::{
|
||||
/// writes through the `write` method. Whenever `write` is called, the component
|
||||
/// that initialized the hook will be marked as "dirty".
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
/// let val = use_ref(|| HashMap::<u32, String>::new());
|
||||
///
|
||||
/// // using `write` will give us a `RefMut` to the inner value, which we can call methods on
|
||||
@@ -26,7 +26,7 @@ use std::{
|
||||
///
|
||||
/// You can avoid this default behavior with `write_silent`
|
||||
///
|
||||
/// ```ignore
|
||||
/// ```rust, no_run
|
||||
/// // with `write_silent`, the component will not be re-rendered
|
||||
/// val.write_silent().insert(2, "goodbye".to_string());
|
||||
/// ```
|
||||
@@ -35,7 +35,7 @@ use std::{
|
||||
///
|
||||
/// To read values out of the refcell, you can use the `read` method which will retrun a `Ref`.
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
/// let map: Ref<_> = val.read();
|
||||
///
|
||||
/// let item = map.get(&1);
|
||||
@@ -43,7 +43,7 @@ use std::{
|
||||
///
|
||||
/// To get an &T out of the RefCell, you need to "reborrow" through the Ref:
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
/// let read = val.read();
|
||||
/// let map = &*read;
|
||||
/// ```
|
||||
@@ -54,10 +54,10 @@ use std::{
|
||||
/// Typically this will be a collection like a HashMap or a Vec. To create new
|
||||
/// elements from the collection, we can use `read()` directly in our rsx!.
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
/// rsx!{
|
||||
/// val.read().iter().map(|(k, v)| {
|
||||
/// rsx!{ key: "{k}", value: "{v}" }
|
||||
/// rsx!{ key: "{k}", "{v}" }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
@@ -66,9 +66,9 @@ use std::{
|
||||
/// "render" inside the iterator. For some cases you might need to collect into
|
||||
/// a temporary Vec.
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
/// let items = val.read().iter().map(|(k, v)| {
|
||||
/// cx.render(rsx!{ key: "{k}", value: "{v}" })
|
||||
/// cx.render(rsx!{ key: "{k}", "{v}" })
|
||||
/// });
|
||||
///
|
||||
/// // collect into a Vec
|
||||
@@ -80,9 +80,9 @@ use std::{
|
||||
///
|
||||
/// To access values from a `UseRef` in an async context, you need to detach it
|
||||
/// from the current scope's lifetime, making it a `'static` value. This is done
|
||||
/// by simply calling `ToOnwed` or `Clone`.
|
||||
/// by simply calling `to_owned` or `clone`.
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
/// let val = use_ref(|| HashMap::<u32, String>::new());
|
||||
///
|
||||
/// cx.spawn({
|
||||
@@ -95,15 +95,15 @@ use std::{
|
||||
/// ```
|
||||
///
|
||||
/// If you're working with lots of values like UseState and UseRef, you can use the
|
||||
/// `clone!` macro to make it easier to write the above code.
|
||||
/// `to_owned!` macro to make it easier to write the above code.
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
/// let val1 = use_ref(|| HashMap::<u32, String>::new());
|
||||
/// let val2 = use_ref(|| HashMap::<u32, String>::new());
|
||||
/// let val3 = use_ref(|| HashMap::<u32, String>::new());
|
||||
///
|
||||
/// cx.spawn({
|
||||
/// clone![val1, val2, val3];
|
||||
/// to_owned![val1, val2, val3];
|
||||
/// async move {
|
||||
/// some_work().await;
|
||||
/// val.write().insert(1, "hello".to_string());
|
||||
@@ -194,7 +194,7 @@ impl<T> UseRef<T> {
|
||||
/// Note: You can always "reborrow" the value through the RefCell.
|
||||
/// This method just does it for you automatically.
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
/// let val = use_ref(|| HashMap::<u32, String>::new());
|
||||
///
|
||||
///
|
||||
@@ -214,7 +214,7 @@ impl<T> UseRef<T> {
|
||||
/// Note: You can always "reborrow" the value through the RefCell.
|
||||
/// This method just does it for you automatically.
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ```rust, no_run
|
||||
/// let val = use_ref(|| HashMap::<u32, String>::new());
|
||||
///
|
||||
///
|
||||
|
||||
Reference in new issue
Block a user