This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew/
context.rs

1//! This module defines the `ContextProvider` component.
2
3use std::cell::RefCell;
4
5use slab::Slab;
6
7use crate::html::Scope;
8use crate::{Callback, Component, Context, Html, Properties};
9
10/// Props for [`ContextProvider`]
11#[derive(Debug, Clone, PartialEq, Properties)]
12pub struct ContextProviderProps<T: Clone + PartialEq> {
13    /// Context value to be passed down
14    pub context: T,
15    /// Children
16    pub children: Html,
17}
18
19/// The context provider component.
20///
21/// Every child (direct or indirect) of this component may access the context value.
22/// In order to consume contexts, [`Scope::context`][Scope::context] method is used,
23/// In function components the `use_context` hook is used.
24#[derive(Debug)]
25pub struct ContextProvider<T: Clone + PartialEq + 'static> {
26    context: T,
27    consumers: RefCell<Slab<Callback<T>>>,
28}
29
30/// Owns the connection to a context provider. When dropped, the component will
31/// no longer receive updates from the provider.
32#[derive(Debug)]
33pub struct ContextHandle<T: Clone + PartialEq + 'static> {
34    provider: Scope<ContextProvider<T>>,
35    key: usize,
36}
37
38impl<T: Clone + PartialEq + 'static> Drop for ContextHandle<T> {
39    fn drop(&mut self) {
40        if let Some(component) = self.provider.get_component() {
41            component.consumers.borrow_mut().remove(self.key);
42        }
43    }
44}
45
46impl<T: Clone + PartialEq> ContextProvider<T> {
47    /// Add the callback to the subscriber list to be called whenever the context changes.
48    /// The consumer is unsubscribed as soon as the callback is dropped.
49    pub(crate) fn subscribe_consumer(
50        &self,
51        callback: Callback<T>,
52        scope: Scope<Self>,
53    ) -> (T, ContextHandle<T>) {
54        let ctx = self.context.clone();
55        let key = self.consumers.borrow_mut().insert(callback);
56
57        (
58            ctx,
59            ContextHandle {
60                provider: scope,
61                key,
62            },
63        )
64    }
65
66    /// Notify all subscribed consumers and remove dropped consumers from the list.
67    fn notify_consumers(&mut self) {
68        let consumers: Vec<Callback<T>> = self
69            .consumers
70            .borrow()
71            .iter()
72            .map(|(_, v)| v.clone())
73            .collect();
74        for consumer in consumers {
75            consumer.emit(self.context.clone());
76        }
77    }
78}
79
80impl<T: Clone + PartialEq + 'static> Component for ContextProvider<T> {
81    type Message = ();
82    type Properties = ContextProviderProps<T>;
83
84    fn create(ctx: &Context<Self>) -> Self {
85        let props = ctx.props();
86        Self {
87            context: props.context.clone(),
88            consumers: RefCell::new(Slab::new()),
89        }
90    }
91
92    fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
93        let props = ctx.props();
94
95        let should_render = old_props.children != props.children;
96
97        if self.context != props.context {
98            self.context = props.context.clone();
99            self.notify_consumers();
100        }
101
102        should_render
103    }
104
105    fn view(&self, ctx: &Context<Self>) -> Html {
106        ctx.props().children.clone()
107    }
108}