use std::cell::RefCell;
use std::fmt;
use std::ops::Deref;
use std::rc::Rc;
use crate::functional::use_hook;
type DispatchFn<T> = Rc<dyn Fn(<T as Reducible>::Action)>;
pub trait Reducible {
type Action;
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self>;
}
struct UseReducer<T>
where
T: Reducible,
{
current_state: Rc<T>,
dispatch: RefCell<Option<DispatchFn<T>>>,
}
pub struct UseReducerHandle<T>
where
T: Reducible,
{
value: Rc<T>,
dispatch: DispatchFn<T>,
}
impl<T> UseReducerHandle<T>
where
T: Reducible,
{
pub fn dispatch(&self, value: T::Action) {
(self.dispatch)(value)
}
pub fn dispatcher(&self) -> UseReducerDispatcher<T> {
UseReducerDispatcher {
dispatch: self.dispatch.clone(),
}
}
}
impl<T> Deref for UseReducerHandle<T>
where
T: Reducible,
{
type Target = T;
fn deref(&self) -> &Self::Target {
&*self.value
}
}
impl<T> Clone for UseReducerHandle<T>
where
T: Reducible,
{
fn clone(&self) -> Self {
Self {
value: Rc::clone(&self.value),
dispatch: Rc::clone(&self.dispatch),
}
}
}
impl<T> fmt::Debug for UseReducerHandle<T>
where
T: Reducible + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UseReducerHandle")
.field("value", &format!("{:?}", self.value))
.finish()
}
}
impl<T> PartialEq for UseReducerHandle<T>
where
T: Reducible + PartialEq,
{
fn eq(&self, rhs: &Self) -> bool {
self.value == rhs.value
}
}
pub struct UseReducerDispatcher<T>
where
T: Reducible,
{
dispatch: DispatchFn<T>,
}
impl<T> Clone for UseReducerDispatcher<T>
where
T: Reducible,
{
fn clone(&self) -> Self {
Self {
dispatch: Rc::clone(&self.dispatch),
}
}
}
impl<T> fmt::Debug for UseReducerDispatcher<T>
where
T: Reducible + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UseReducerDispatcher").finish()
}
}
impl<T> PartialEq for UseReducerDispatcher<T>
where
T: Reducible,
{
fn eq(&self, rhs: &Self) -> bool {
#[allow(clippy::vtable_address_comparisons)]
Rc::ptr_eq(&self.dispatch, &rhs.dispatch)
}
}
impl<T> UseReducerDispatcher<T>
where
T: Reducible,
{
pub fn dispatch(&self, value: T::Action) {
(self.dispatch)(value)
}
}
fn use_reducer_base<T, F, R>(initial_fn: F, should_render_fn: R) -> UseReducerHandle<T>
where
T: Reducible + 'static,
F: FnOnce() -> T,
R: (Fn(&T, &T) -> bool) + 'static,
{
use_hook(
move || UseReducer {
current_state: Rc::new(initial_fn()),
dispatch: RefCell::default(),
},
|s, updater| {
let mut dispatch_ref = s.dispatch.borrow_mut();
let dispatch = match *dispatch_ref {
Some(ref m) => (*m).to_owned(),
None => {
let should_render_fn = Rc::new(should_render_fn);
let dispatch: Rc<dyn Fn(T::Action)> = Rc::new(move |action: T::Action| {
let should_render_fn = should_render_fn.clone();
updater.callback(move |state: &mut UseReducer<T>| {
let next_state = state.current_state.clone().reduce(action);
let should_render = should_render_fn(&next_state, &state.current_state);
state.current_state = next_state;
should_render
});
});
*dispatch_ref = Some(dispatch.clone());
dispatch
}
};
UseReducerHandle {
value: Rc::clone(&s.current_state),
dispatch,
}
},
|_| {},
)
}
pub fn use_reducer<T, F>(initial_fn: F) -> UseReducerHandle<T>
where
T: Reducible + 'static,
F: FnOnce() -> T,
{
use_reducer_base(initial_fn, |_, _| true)
}
pub fn use_reducer_eq<T, F>(initial_fn: F) -> UseReducerHandle<T>
where
T: Reducible + PartialEq + 'static,
F: FnOnce() -> T,
{
use_reducer_base(initial_fn, T::ne)
}