Merge branch 'master' into feature/use-shared-state-better-diagnostics

This commit is contained in:
Jonathan Kelley authored and GitHub committed 2023-07-20 10:51:10 -07:00
commit 91d4207fa7
324 files changed
+13509 -6646

No files matched your search

+23 -7
View File
@@ -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)
+21 -94
View File
@@ -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!()
}
}
+15 -9
View File
@@ -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
+15 -15
View File
@@ -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());
///
///