Skip to content

Commit

Permalink
Split update and render operations into seprate tasks (yewstack#1309)
Browse files Browse the repository at this point in the history
* Split update and render operations into seprate tasks

* Fix functional components

* Rename internal functional component terms
  • Loading branch information
jstarry authored and teymour-aldridge committed Jun 20, 2020
1 parent 79c33b7 commit 36b4b16
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 248 deletions.
119 changes: 80 additions & 39 deletions yew-functional/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ pub trait Hook {
fn tear_down(&mut self) {}
}

type ProcessMessage = Rc<dyn Fn(Box<dyn FnOnce() -> bool>)>;
type Msg = Box<dyn FnOnce() -> bool>;
type ProcessMessage = Rc<dyn Fn(Msg, bool)>;

struct HookState {
counter: usize,
scope: AnyScope,
Expand All @@ -30,10 +32,25 @@ pub trait FunctionProvider {
fn run(props: &Self::TProps) -> Html;
}

pub struct FunctionComponent<T: FunctionProvider> {
#[derive(Clone, Default)]
struct MsgQueue(Rc<RefCell<Vec<Msg>>>);

impl MsgQueue {
fn push(&self, msg: Msg) {
self.0.borrow_mut().push(msg);
}

fn drain(&self) -> Vec<Msg> {
self.0.borrow_mut().drain(..).collect()
}
}

pub struct FunctionComponent<T: FunctionProvider + 'static> {
_never: std::marker::PhantomData<T>,
props: T::TProps,
link: ComponentLink<Self>,
hook_state: RefCell<Option<HookState>>,
message_queue: MsgQueue,
}

impl<T> FunctionComponent<T>
Expand Down Expand Up @@ -62,19 +79,34 @@ where

fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
let scope = AnyScope::from(link.clone());
let message_queue = MsgQueue::default();
FunctionComponent {
_never: std::marker::PhantomData::default(),
props,
link: link.clone(),
message_queue: message_queue.clone(),
hook_state: RefCell::new(Some(HookState {
counter: 0,
scope,
process_message: Rc::new(move |msg| link.send_message(msg)),
process_message: Rc::new(move |msg, post_render| {
if post_render {
message_queue.push(msg);
} else {
link.send_message(msg);
}
}),
hooks: vec![],
destroy_listeners: vec![],
})),
}
}

fn rendered(&mut self, _first_render: bool) {
for msg in self.message_queue.drain() {
self.link.send_message(msg);
}
}

fn update(&mut self, msg: Self::Message) -> bool {
msg()
}
Expand Down Expand Up @@ -128,8 +160,9 @@ where
impl<T> Hook for UseRefState<T> {}

use_hook(
|state: &mut UseRefState<T>, pretrigger_change_acceptor| {
let _ignored = || pretrigger_change_acceptor(|_| false); // we need it to be a specific closure type, even if we never use it
|state: &mut UseRefState<T>, hook_callback| {
// we need it to be a specific closure type, even if we never use it
let _ignored = || hook_callback(|_| false, false);
state.0.clone()
},
move || UseRefState(Rc::new(RefCell::new(initial_value()))),
Expand Down Expand Up @@ -162,19 +195,20 @@ where
let init = Box::new(init);
let reducer = Rc::new(reducer);
use_hook(
|internal_hook_change: &mut UseReducerState<State>, pretrigger_change_runner| {
|internal_hook_change: &mut UseReducerState<State>, hook_callback| {
(
internal_hook_change.current_state.clone(),
Rc::new(move |action: Action| {
let reducer = reducer.clone();
pretrigger_change_runner(
hook_callback(
move |internal_hook_change: &mut UseReducerState<State>| {
internal_hook_change.current_state = Rc::new((reducer)(
internal_hook_change.current_state.clone(),
action,
));
true
},
false, // run pre render
);
}),
)
Expand All @@ -195,15 +229,18 @@ where
}
impl<T> Hook for UseStateState<T> {}
use_hook(
|prev: &mut UseStateState<T>, hook_update| {
|prev: &mut UseStateState<T>, hook_callback| {
let current = prev.current.clone();
(
current,
Box::new(move |o: T| {
hook_update(|state: &mut UseStateState<T>| {
state.current = Rc::new(o);
true
});
hook_callback(
|state: &mut UseStateState<T>| {
state.current = Rc::new(o);
true
},
false, // run pre render
)
}),
)
},
Expand All @@ -230,20 +267,21 @@ where
}
}
use_hook(
|_: &mut UseEffectState<Destructor>, hook_update| {
move || {
hook_update(move |state: &mut UseEffectState<Destructor>| {
|_: &mut UseEffectState<Destructor>, hook_callback| {
hook_callback(
move |state: &mut UseEffectState<Destructor>| {
if let Some(de) = state.destructor.take() {
de();
}
let new_destructor = callback();
state.destructor.replace(Box::new(new_destructor));
false
});
}
},
true, // run post render
);
},
|| UseEffectState { destructor: None },
)();
);
}

pub fn use_effect_with_deps<F, Destructor, Dependents>(callback: F, deps: Dependents)
Expand All @@ -267,11 +305,10 @@ where
}
}
use_hook(
move |state: &mut UseEffectState<Dependents, Destructor>, hook_update| {
move |state: &mut UseEffectState<Dependents, Destructor>, hook_callback| {
let mut should_update = *state.deps != *deps;

move || {
hook_update(move |state: &mut UseEffectState<Dependents, Destructor>| {
hook_callback(
move |state: &mut UseEffectState<Dependents, Destructor>| {
if should_update {
if let Some(de) = state.destructor.take() {
de();
Expand All @@ -286,25 +323,26 @@ where
.replace(Box::new(callback(state.deps.borrow())));
}
false
})
}
},
true, // run post render
);
},
|| UseEffectState {
deps: deps_c,
destructor: None,
},
)();
);
}

pub fn use_hook<InternalHookState, HookRunner, R, InitialStateProvider, PretriggerChange: 'static>(
pub fn use_hook<InternalHookState, HookRunner, R, InitialStateProvider, HookUpdate: 'static>(
hook_runner: HookRunner,
initial_state_producer: InitialStateProvider,
) -> R
where
HookRunner: FnOnce(&mut InternalHookState, Box<dyn Fn(PretriggerChange)>) -> R,
HookRunner: FnOnce(&mut InternalHookState, Box<dyn Fn(HookUpdate, bool)>) -> R,
InternalHookState: Hook + 'static,
InitialStateProvider: FnOnce() -> InternalHookState,
PretriggerChange: FnOnce(&mut InternalHookState) -> bool,
HookUpdate: FnOnce(&mut InternalHookState) -> bool,
{
// Extract current hook
let (hook, process_message) = CURRENT_HOOK.with(|hook_state_holder| {
Expand Down Expand Up @@ -332,18 +370,21 @@ where
(hook, hook_state.process_message.clone())
});

let trigger = {
let hook_callback = {
let hook = hook.clone();
Box::new(move |pretrigger_change: PretriggerChange| {
Box::new(move |update: HookUpdate, post_render| {
let hook = hook.clone();
process_message(Box::new(move || {
let mut hook = hook.borrow_mut();
let hook = hook.downcast_mut::<InternalHookState>();
let hook = hook.expect(
"Incompatible hook type. Hooks must always be called in the same order",
);
pretrigger_change(hook)
}));
process_message(
Box::new(move || {
let mut hook = hook.borrow_mut();
let hook = hook.downcast_mut::<InternalHookState>();
let hook = hook.expect(
"Incompatible hook type. Hooks must always be called in the same order",
);
update(hook)
}),
post_render,
);
})
};
let mut hook = hook.borrow_mut();
Expand All @@ -353,7 +394,7 @@ where

// Execute the actual hook closure we were given. Let it mutate the hook state and let
// it create a callback that takes the mutable hook state.
hook_runner(&mut hook, trigger)
hook_runner(&mut hook, hook_callback)
}

pub(crate) fn get_current_scope() -> Option<AnyScope> {
Expand Down
13 changes: 8 additions & 5 deletions yew-functional/src/use_context_hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,15 @@ pub fn use_context<T: 'static + Clone>() -> Option<Rc<T>> {
}
}
use_hook(
|state: &mut UseContextState<T>, hook_update| {
|state: &mut UseContextState<T>, hook_callback| {
state.callback = Some(Rc::new(Box::new(move |ctx: Rc<T>| {
hook_update(|state: &mut UseContextState<T>| {
state.current_context = Some(ctx);
true
});
hook_callback(
|state: &mut UseContextState<T>| {
state.current_context = Some(ctx);
true
},
false, // run pre render
);
})));
let weak_cb = Rc::downgrade(state.callback.as_ref().unwrap());
with_provider_component(&state.provider_scope, |comp| {
Expand Down
Loading

0 comments on commit 36b4b16

Please sign in to comment.