Merge branch 'DioxusLabs:master' into cli-stuff

This commit is contained in:
Miles Murgaw authored and GitHub committed 2023-07-14 19:40:22 -04:00
commit bfbca2653f
23 files changed
+260 -114

No files matched your search

+10 -10
View File
@@ -1,4 +1,3 @@
<div align="center">
<h1>Fermi ⚛</h1>
<p>
@@ -6,7 +5,6 @@
</p>
</div>
<div align="center">
<!-- Crates version -->
<a href="https://crates.io/crates/fermi">
@@ -30,21 +28,21 @@
</a>
</div>
-----
---
Fermi is a global state management solution for Dioxus that's as easy as `use_state`.
Inspired by atom-based state management solutions, all state in Fermi starts as an `atom`:
```rust, ignore
static NAME: Atom<&str> = |_| "Dioxus";
static NAME: Atom<&str> = Atom(|_| "Dioxus");
```
From anywhere in our app, we can read the value of our atom:
```rust, ignores
```rust, ignore
fn NameCard(cx: Scope) -> Element {
let name = use_read(cx, NAME);
let name = use_read(cx, &NAME);
cx.render(rsx!{ h1 { "Hello, {name}"} })
}
```
@@ -53,7 +51,7 @@ We can also set the value of our atom, also from anywhere in our app:
```rust, ignore
fn NameCard(cx: Scope) -> Element {
let set_name = use_set(cx, NAME);
let set_name = use_set(cx, &NAME);
cx.render(rsx!{
button {
onclick: move |_| set_name("Fermi"),
@@ -66,10 +64,10 @@ fn NameCard(cx: Scope) -> Element {
If needed, we can update the atom's value, based on itself:
```rust, ignore
static COUNT: Atom<i32> = |_| 0;
static COUNT: Atom<i32> = Atom(|_| 0);
fn Counter(cx: Scope) -> Element {
let mut count = use_atom_state(cx, COUNT);
let mut count = use_atom_state(cx, &COUNT);
cx.render(rsx!{
p {
@@ -86,6 +84,7 @@ fn Counter(cx: Scope) -> Element {
It's that simple!
## Installation
Fermi is currently under construction, so you have to use the `master` branch to get started.
```toml
@@ -93,10 +92,10 @@ Fermi is currently under construction, so you have to use the `master` branch to
fermi = { git = "https://github.com/dioxuslabs/dioxus" }
```
## Running examples
The examples here use Dioxus Desktop to showcase their functionality. To run an example, use
```sh
$ cargo run --example fermi
```
@@ -104,6 +103,7 @@ $ cargo run --example fermi
## Features
Broadly our feature set required to be released includes:
- [x] Support for Atoms
- [x] Support for AtomRef (for values that aren't `Clone`)
- [ ] Support for Atom Families
+23 -10
View File
@@ -1,24 +1,21 @@
use crate::{AtomId, AtomRoot, Readable, Writable};
pub type Atom<T> = fn(AtomBuilder) -> T;
pub struct Atom<T>(pub fn(AtomBuilder) -> T);
pub struct AtomBuilder;
impl<V: 'static> Readable<V> for Atom<V> {
impl<V> Readable<V> for &'static Atom<V> {
fn read(&self, _root: AtomRoot) -> Option<V> {
todo!()
}
fn init(&self) -> V {
(*self)(AtomBuilder)
self.0(AtomBuilder)
}
fn unique_id(&self) -> AtomId {
AtomId {
ptr: *self as *const (),
type_id: std::any::TypeId::of::<V>(),
}
*self as *const Atom<V> as *const ()
}
}
impl<V: 'static> Writable<V> for Atom<V> {
impl<V> Writable<V> for &'static Atom<V> {
fn write(&self, _root: AtomRoot, _value: V) {
todo!()
}
@@ -26,6 +23,22 @@ impl<V: 'static> Writable<V> for Atom<V> {
#[test]
fn atom_compiles() {
static TEST_ATOM: Atom<&str> = |_| "hello";
dbg!(TEST_ATOM.init());
static TEST_ATOM: Atom<&str> = Atom(|_| "hello");
dbg!((&TEST_ATOM).init());
}
#[test]
fn atom_is_unique() {
static TEST_ATOM_1: Atom<&str> = Atom(|_| "hello");
static TEST_ATOM_2: Atom<&str> = Atom(|_| "hello");
assert_eq!((&TEST_ATOM_1).unique_id(), (&TEST_ATOM_1).unique_id());
assert_ne!((&TEST_ATOM_1).unique_id(), (&TEST_ATOM_2).unique_id());
}
#[test]
fn atom_is_unique_2() {
struct S(String);
static TEST_ATOM_1: Atom<Vec<S>> = Atom(|_| Vec::new());
static TEST_ATOM_2: Atom<Vec<String>> = Atom(|_| Vec::new());
assert_ne!((&TEST_ATOM_1).unique_id(), (&TEST_ATOM_2).unique_id());
}
+5 -8
View File
@@ -2,26 +2,23 @@ use crate::{AtomId, AtomRoot, Readable, Writable};
use im_rc::HashMap as ImMap;
pub struct AtomFamilyBuilder;
pub type AtomFamily<K, V> = fn(AtomFamilyBuilder) -> ImMap<K, V>;
pub struct AtomFamily<K, V>(pub fn(AtomFamilyBuilder) -> ImMap<K, V>);
impl<K, V: 'static> Readable<ImMap<K, V>> for AtomFamily<K, V> {
impl<K, V> Readable<ImMap<K, V>> for &'static AtomFamily<K, V> {
fn read(&self, _root: AtomRoot) -> Option<ImMap<K, V>> {
todo!()
}
fn init(&self) -> ImMap<K, V> {
(*self)(AtomFamilyBuilder)
self.0(AtomFamilyBuilder)
}
fn unique_id(&self) -> AtomId {
AtomId {
ptr: *self as *const (),
type_id: std::any::TypeId::of::<V>(),
}
*self as *const AtomFamily<K, V> as *const ()
}
}
impl<K, V: 'static> Writable<ImMap<K, V>> for AtomFamily<K, V> {
impl<K, V> Writable<ImMap<K, V>> for &'static AtomFamily<K, V> {
fn write(&self, _root: AtomRoot, _value: ImMap<K, V>) {
todo!()
}
+6 -9
View File
@@ -2,27 +2,24 @@ use crate::{AtomId, AtomRoot, Readable};
use std::cell::RefCell;
pub struct AtomRefBuilder;
pub type AtomRef<T> = fn(AtomRefBuilder) -> T;
pub struct AtomRef<T>(pub fn(AtomRefBuilder) -> T);
impl<V: 'static> Readable<RefCell<V>> for AtomRef<V> {
impl<V> Readable<RefCell<V>> for &'static AtomRef<V> {
fn read(&self, _root: AtomRoot) -> Option<RefCell<V>> {
todo!()
}
fn init(&self) -> RefCell<V> {
RefCell::new((*self)(AtomRefBuilder))
RefCell::new(self.0(AtomRefBuilder))
}
fn unique_id(&self) -> AtomId {
AtomId {
ptr: *self as *const (),
type_id: std::any::TypeId::of::<V>(),
}
*self as *const AtomRef<V> as *const ()
}
}
#[test]
fn atom_compiles() {
static TEST_ATOM: AtomRef<Vec<String>> = |_| vec![];
dbg!(TEST_ATOM.init());
static TEST_ATOM: AtomRef<Vec<String>> = AtomRef(|_| vec![]);
dbg!((&TEST_ATOM).init());
}
+4 -1
View File
@@ -13,7 +13,10 @@ use std::{
///
///
///
pub fn use_atom_ref<T: 'static>(cx: &ScopeState, atom: AtomRef<T>) -> &UseAtomRef<T> {
pub fn use_atom_ref<'a, T: 'static>(
cx: &'a ScopeState,
atom: &'static AtomRef<T>,
) -> &'a UseAtomRef<T> {
let root = use_atom_root(cx);
&cx.use_hook(|| {
+1 -1
View File
@@ -19,7 +19,7 @@ use std::{
/// static COUNT: Atom<u32> = |_| 0;
///
/// fn Example(cx: Scope) -> Element {
/// let mut count = use_atom_state(cx, COUNT);
/// let mut count = use_atom_state(cx, &COUNT);
///
/// cx.render(rsx! {
/// div {
+3 -21
View File
@@ -1,21 +1,11 @@
use std::{
any::{Any, TypeId},
cell::RefCell,
collections::HashMap,
rc::Rc,
sync::Arc,
};
use std::{any::Any, cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
use dioxus_core::ScopeId;
use im_rc::HashSet;
use crate::Readable;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AtomId {
pub ptr: *const (),
pub type_id: TypeId,
}
pub type AtomId = *const ();
pub struct AtomRoot {
pub atoms: RefCell<HashMap<AtomId, Slot>>,
@@ -54,15 +44,7 @@ impl AtomRoot {
// initialize the value if it's not already initialized
if let Some(slot) = atoms.get_mut(&f.unique_id()) {
slot.subscribers.insert(scope);
match slot.value.clone().downcast() {
Ok(res) => res,
Err(e) => panic!(
"Downcasting atom failed: {:?}. Has typeid of {:?} but needs typeid of {:?}",
f.unique_id(),
e.type_id(),
TypeId::of::<V>()
),
}
slot.value.clone().downcast().unwrap()
} else {
let value = Rc::new(f.init());
let mut subscribers = HashSet::new();
+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)
+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