Skip to content

Commit

Permalink
move props inside Context
Browse files Browse the repository at this point in the history
  • Loading branch information
ranile committed Jul 20, 2021
1 parent 7bc35e1 commit 14d3845
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 141 deletions.
38 changes: 13 additions & 25 deletions packages/yew/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::html::Scope;
use crate::{html, Callback, Children, Component, Html, Properties, ShouldRender};
use slab::Slab;
use std::cell::RefCell;
use std::rc::Rc;

/// Props for [`ContextProvider`]
#[derive(Debug, Clone, PartialEq, Properties)]
Expand All @@ -23,9 +22,7 @@ pub struct ContextProviderProps<T: Clone + PartialEq> {
/// In function components the `use_context` hook is used.
#[derive(Debug)]
pub struct ContextProvider<T: Clone + PartialEq + 'static> {
component_context: Context<Self>,
context: T,
children: Children,
consumers: RefCell<Slab<Callback<T>>>,
}

Expand All @@ -48,14 +45,18 @@ impl<T: Clone + PartialEq + 'static> Drop for ContextHandle<T> {
impl<T: Clone + PartialEq> ContextProvider<T> {
/// Add the callback to the subscriber list to be called whenever the context changes.
/// The consumer is unsubscribed as soon as the callback is dropped.
pub(crate) fn subscribe_consumer(&self, callback: Callback<T>) -> (T, ContextHandle<T>) {
pub(crate) fn subscribe_consumer(
&self,
callback: Callback<T>,
scope: Scope<Self>,
) -> (T, ContextHandle<T>) {
let ctx = self.context.clone();
let key = self.consumers.borrow_mut().insert(callback);

(
ctx,
ContextHandle {
provider: self.component_context.clone(),
provider: scope,
key,
},
)
Expand All @@ -79,11 +80,9 @@ impl<T: Clone + PartialEq + 'static> Component for ContextProvider<T> {
type Message = ();
type Properties = ContextProviderProps<T>;

fn create(props: Rc<Self::Properties>, ctx: &Context<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
Self {
component_context: ctx.clone(),
children: props.children.clone(),
context: props.context.clone(),
context: ctx.props().context.clone(),
consumers: RefCell::new(Slab::new()),
}
}
Expand All @@ -92,23 +91,12 @@ impl<T: Clone + PartialEq + 'static> Component for ContextProvider<T> {
true
}

fn changed(&mut self, _ctx: &Context<Self>, props: Rc<Self::Properties>) -> bool {
let should_render = if self.children == props.children {
false
} else {
self.children = props.children.clone();
true
};

if self.context != props.context {
self.context = props.context.clone();
self.notify_consumers();
}

should_render
fn changed(&mut self, _ctx: &Context<Self>) -> bool {
self.notify_consumers();
true
}

fn view(&self, _ctx: &Context<Self>) -> Html {
html! { <>{ self.children.clone() }</> }
fn view(&self, ctx: &Context<Self>) -> Html {
html! { <>{ ctx.props().children.clone() }</> }
}
}
21 changes: 7 additions & 14 deletions packages/yew/src/functional/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ pub trait FunctionProvider {
/// Wrapper that allows a struct implementing [`FunctionProvider`] to be consumed as a component.
pub struct FunctionComponent<T: FunctionProvider + 'static> {
_never: std::marker::PhantomData<T>,
props: Rc<T::TProps>,
hook_state: RefCell<HookState>,
message_queue: MsgQueue,
}
Expand Down Expand Up @@ -108,24 +107,23 @@ where
type Message = Box<dyn FnOnce() -> bool>;
type Properties = T::TProps;

fn create(props: Rc<Self::Properties>, ctx: &Context<Self>) -> Self {
let scope = AnyScope::from(ctx.clone());
fn create(ctx: &Context<Self>) -> Self {
let scope = AnyScope::from(ctx.link().clone());
let message_queue = MsgQueue::default();

Self {
_never: std::marker::PhantomData::default(),
props: Rc::clone(&props),
message_queue: message_queue.clone(),
hook_state: RefCell::new(HookState {
counter: 0,
scope,
process_message: {
let ctx = ctx.clone();
let scope = ctx.link().clone();
Rc::new(move |msg, post_render| {
if post_render {
message_queue.push(msg);
} else {
ctx.send_message(msg);
scope.send_message(msg);
}
})
},
Expand All @@ -139,18 +137,13 @@ where
msg()
}

fn changed(&mut self, _ctx: &Context<Self>, props: Rc<Self::Properties>) -> bool {
self.props = props;
true
}

fn view(&self, _ctx: &Context<Self>) -> Html {
self.with_hook_state(|| T::run(&self.props))
fn view(&self, ctx: &Context<Self>) -> Html {
self.with_hook_state(|| T::run(&*ctx.props()))
}

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

Expand Down
87 changes: 47 additions & 40 deletions packages/yew/src/html/component/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
use super::{Component, Scope};
use crate::scheduler::{self, Runnable, Shared};
use crate::virtual_dom::{VDiff, VNode};
use crate::NodeRef;
use crate::{Context, NodeRef};
use std::rc::Rc;
use web_sys::Element;

pub(crate) struct ComponentState<COMP: Component> {
pub(crate) component: Box<COMP>,
pub(crate) root_node: VNode,

scope: Scope<COMP>,
context: Context<COMP>,
parent: Element,
next_sibling: NodeRef,
node_ref: NodeRef,
Expand All @@ -29,11 +29,13 @@ impl<COMP: Component> ComponentState<COMP> {
scope: Scope<COMP>,
props: Rc<COMP::Properties>,
) -> Self {
let component = Box::new(COMP::create(Rc::clone(&props), &scope));
let context = Context { scope, props };

let component = Box::new(COMP::create(&context));
Self {
component,
root_node,
scope,
context,
parent,
next_sibling,
node_ref,
Expand Down Expand Up @@ -127,25 +129,26 @@ impl<COMP: Component> Runnable for ComponentRunnable<COMP> {
let should_render = match event {
UpdateEvent::First => true,
UpdateEvent::Message(message) => {
state.component.update(&state.scope, message)
state.component.update(&state.context, message)
}
UpdateEvent::MessageBatch(messages) => {
messages.into_iter().fold(false, |acc, msg| {
state.component.update(&state.scope, msg) || acc
state.component.update(&state.context, msg) || acc
})
}
UpdateEvent::Properties(props, node_ref, next_sibling) => {
// When components are updated, a new node ref could have been passed in
state.node_ref = node_ref;
// When components are updated, their siblings were likely also updated
state.next_sibling = next_sibling;
state.component.changed(&state.scope, Rc::clone(&props))
state.context.props = Rc::clone(&props);
state.component.changed(&state.context)
}
};

if should_render {
state.pending_root = Some(state.component.view(&state.scope));
state.scope.process(ComponentLifecycleEvent::Render);
state.pending_root = Some(state.component.view(&state.context));
state.context.scope.process(ComponentLifecycleEvent::Render);
};
}
}
Expand All @@ -155,25 +158,28 @@ impl<COMP: Component> Runnable for ComponentRunnable<COMP> {
std::mem::swap(&mut new_root, &mut state.root_node);
let ancestor = Some(new_root);
let new_root = &mut state.root_node;
let scope = state.scope.clone().into();
let scope = state.context.scope.clone().into();
let next_sibling = state.next_sibling.clone();
let node = new_root.apply(&scope, &state.parent, next_sibling, ancestor);
state.node_ref.link(node);
state.scope.process(ComponentLifecycleEvent::Rendered);
state
.context
.scope
.process(ComponentLifecycleEvent::Rendered);
}
}
}
ComponentLifecycleEvent::Rendered => {
if let Some(mut state) = current_state.as_mut() {
let first_render = !state.has_rendered;
state.component.rendered(&state.scope, first_render);
state.component.rendered(&state.context, first_render);
state.has_rendered = true;
state.drain_pending_updates(&self.state);
}
}
ComponentLifecycleEvent::Destroy => {
if let Some(mut state) = current_state.take() {
state.component.destroy(&state.scope);
state.component.destroy(&state.context);
state.root_node.detach(&state.parent);
state.node_ref.set(None);
}
Expand Down Expand Up @@ -201,20 +207,18 @@ mod tests {
lifecycle: Rc<RefCell<Vec<String>>>,
}

struct Child {
props: Rc<ChildProps>,
}
struct Child {}

impl Component for Child {
type Message = ();
type Properties = ChildProps;

fn create(props: Rc<Self::Properties>, _ctx: &Context<Self>) -> Self {
Child { props }
fn create(_ctx: &Context<Self>) -> Self {
Child {}
}

fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
self.props
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
ctx.props()
.lifecycle
.borrow_mut()
.push("child rendered".into());
Expand All @@ -224,7 +228,7 @@ mod tests {
false
}

fn changed(&mut self, _ctx: &Context<Self>, _: Rc<Self::Properties>) -> ShouldRender {
fn changed(&mut self, _ctx: &Context<Self>) -> ShouldRender {
false
}

Expand All @@ -245,60 +249,63 @@ mod tests {
}

struct Comp {
props: Rc<Props>,
lifecycle: Rc<RefCell<Vec<String>>>,
}

impl Component for Comp {
type Message = bool;
type Properties = Props;

fn create(props: Rc<Self::Properties>, ctx: &Context<Self>) -> Self {
props.lifecycle.borrow_mut().push("create".into());
fn create(ctx: &Context<Self>) -> Self {
ctx.props().lifecycle.borrow_mut().push("create".into());
#[cfg(feature = "wasm_test")]
if let Some(msg) = props.create_message {
ctx.send_message(msg);
if let Some(msg) = ctx.props().create_message {
ctx.link().send_message(msg);
}
Comp {
lifecycle: Rc::clone(&ctx.props().lifecycle),
}
Comp { props }
}

fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
if let Some(msg) = self.props.rendered_message.borrow_mut().take() {
ctx.send_message(msg);
if let Some(msg) = ctx.props().rendered_message.borrow_mut().take() {
ctx.link().send_message(msg);
}
self.props
ctx.props()
.lifecycle
.borrow_mut()
.push(format!("rendered({})", first_render));
}

fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> ShouldRender {
if let Some(msg) = self.props.update_message.borrow_mut().take() {
ctx.send_message(msg);
if let Some(msg) = ctx.props().update_message.borrow_mut().take() {
ctx.link().send_message(msg);
}
self.props
ctx.props()
.lifecycle
.borrow_mut()
.push(format!("update({})", msg));
msg
}

fn changed(&mut self, _ctx: &Context<Self>, _: Rc<Self::Properties>) -> ShouldRender {
self.props.lifecycle.borrow_mut().push("change".into());
fn changed(&mut self, ctx: &Context<Self>) -> ShouldRender {
self.lifecycle = Rc::clone(&ctx.props().lifecycle);
self.lifecycle.borrow_mut().push("change".into());
false
}

fn view(&self, ctx: &Context<Self>) -> Html {
if let Some(msg) = self.props.view_message.borrow_mut().take() {
ctx.send_message(msg);
if let Some(msg) = ctx.props().view_message.borrow_mut().take() {
ctx.link().send_message(msg);
}
self.props.lifecycle.borrow_mut().push("view".into());
html! { <Child lifecycle={self.props.lifecycle.clone()} /> }
self.lifecycle.borrow_mut().push("view".into());
html! { <Child lifecycle={self.lifecycle.clone()} /> }
}
}

impl Drop for Comp {
fn drop(&mut self) {
self.props.lifecycle.borrow_mut().push("drop".into());
self.lifecycle.borrow_mut().push("drop".into());
}
}

Expand Down
24 changes: 21 additions & 3 deletions packages/yew/src/html/component/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,37 @@ use std::rc::Rc;
pub type ShouldRender = bool;

/// Context
pub type Context<T> = Scope<T>;
#[derive(Debug)]
pub struct Context<COMP: Component> {
pub(crate) scope: Scope<COMP>,
pub(crate) props: Rc<COMP::Properties>,
}

impl<COMP: Component> Context<COMP> {
/// The component link
#[inline]
pub fn link(&self) -> &Scope<COMP> {
&self.scope
}

/// The component's props
#[inline]
pub fn props(&self) -> &COMP::Properties {
&*self.props
}
}

#[allow(missing_docs)]
/// Yew component
pub trait Component: Sized + 'static {
type Message: 'static;
type Properties: Properties;

fn create(props: Rc<Self::Properties>, ctx: &Context<Self>) -> Self;
fn create(ctx: &Context<Self>) -> Self;
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> ShouldRender {
false
}
fn changed(&mut self, _ctx: &Context<Self>, _new_props: Rc<Self::Properties>) -> ShouldRender {
fn changed(&mut self, _ctx: &Context<Self>) -> ShouldRender {
true
}
fn view(&self, ctx: &Context<Self>) -> Html;
Expand Down
Loading

0 comments on commit 14d3845

Please sign in to comment.