wip: use_reducer
This commit is contained in:
parent
9237d02cf2
commit
879e107634
|
@ -117,3 +117,97 @@ mod use_ref_def {
|
|||
ctx.use_hook(|| UseRef::new(initial_state_fn()), |state| &*state, |_| {})
|
||||
}
|
||||
}
|
||||
|
||||
mod use_reducer_def {
|
||||
use crate::innerlude::*;
|
||||
use std::{cell::RefCell, ops::DerefMut, rc::Rc};
|
||||
|
||||
struct UseReducer<T: 'static, R: 'static> {
|
||||
new_val: Rc<RefCell<Option<T>>>,
|
||||
current_val: T,
|
||||
caller: Box<dyn Fn(R) + 'static>,
|
||||
}
|
||||
|
||||
/// Store state between component renders!
|
||||
/// When called, this hook retrives a stored value and provides a setter to update that value.
|
||||
/// When the setter is called, the component is re-ran with the new value.
|
||||
///
|
||||
/// This is behaves almost exactly the same way as React's "use_state".
|
||||
///
|
||||
pub fn use_reducer<'a, 'c, State: 'static, Action: 'static>(
|
||||
ctx: &'c Context<'a>,
|
||||
initial_state_fn: impl FnOnce() -> State,
|
||||
reducer: impl Fn(&mut State, Action),
|
||||
) -> (&'a State, &'a impl Fn(Action)) {
|
||||
ctx.use_hook(
|
||||
move || UseReducer {
|
||||
new_val: Rc::new(RefCell::new(None)),
|
||||
current_val: initial_state_fn(),
|
||||
caller: Box::new(|_| println!("setter called!")),
|
||||
},
|
||||
move |hook| {
|
||||
let inner = hook.new_val.clone();
|
||||
let scheduled_update = ctx.schedule_update();
|
||||
|
||||
// get ownership of the new val and replace the current with the new
|
||||
// -> as_ref -> borrow_mut -> deref_mut -> take
|
||||
// -> rc -> &RefCell -> RefMut -> &Option<T> -> T
|
||||
if let Some(new_val) = hook.new_val.as_ref().borrow_mut().deref_mut().take() {
|
||||
hook.current_val = new_val;
|
||||
}
|
||||
|
||||
// todo: swap out the caller with a subscription call and an internal update
|
||||
hook.caller = Box::new(move |new_val| {
|
||||
// update the setter with the new value
|
||||
// let mut new_inner = inner.as_ref().borrow_mut();
|
||||
// *new_inner = Some(new_val);
|
||||
|
||||
// Ensure the component gets updated
|
||||
scheduled_update();
|
||||
});
|
||||
|
||||
// box gets derefed into a ref which is then taken as ref with the hook
|
||||
(&hook.current_val, &hook.caller)
|
||||
},
|
||||
|_| {},
|
||||
)
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::prelude::*;
|
||||
use bumpalo::Bump;
|
||||
|
||||
enum Actions {
|
||||
Incr,
|
||||
Decr,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
static Example: FC<()> = |ctx, props| {
|
||||
let (count, reduce) = use_reducer(
|
||||
&ctx,
|
||||
|| 0,
|
||||
|count, action| match action {
|
||||
Actions::Incr => *count += 1,
|
||||
Actions::Decr => *count -= 1,
|
||||
},
|
||||
);
|
||||
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
h1 {"Count: {count}"}
|
||||
button {
|
||||
"Increment"
|
||||
onclick: move |_| reduce(Actions::Incr)
|
||||
}
|
||||
button {
|
||||
"Decrement"
|
||||
onclick: move |_| reduce(Actions::Decr)
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue