From 879e107634f596d555f62f8853afcf5b3796b180 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 2 Mar 2021 02:27:07 -0500 Subject: [PATCH] wip: use_reducer --- packages/core/src/hooks.rs | 94 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/packages/core/src/hooks.rs b/packages/core/src/hooks.rs index 5236c45c0..4632284bc 100644 --- a/packages/core/src/hooks.rs +++ b/packages/core/src/hooks.rs @@ -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 { + new_val: Rc>>, + current_val: T, + caller: Box, + } + + /// 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 + 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) + } + } + }) + }; + } +}