1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use crate::context::ContextHandle;
use crate::functional::{get_current_scope, use_hook};

/// Hook for consuming context values in function components.
/// The context of the type passed as `T` is returned. If there is no such context in scope, `None` is returned.
/// A component which calls `use_context` will re-render when the data of the context changes.
///
/// More information about contexts and how to define and consume them can be found on [Yew Docs](https://yew.rs).
///
/// # Example
/// ```rust
/// # use yew::prelude::*;
/// # use std::rc::Rc;
///
/// # #[derive(Clone, Debug, PartialEq)]
/// # struct ThemeContext {
/// #    foreground: String,
/// #    background: String,
/// # }
/// #[function_component(ThemedButton)]
/// pub fn themed_button() -> Html {
///     let theme = use_context::<Rc<ThemeContext>>().expect("no ctx found");
///
///     html! {
///         <button style={format!("background: {}; color: {}", theme.background, theme.foreground)}>
///             { "Click me" }
///         </button>
///     }
/// }
/// ```
pub fn use_context<T: Clone + PartialEq + 'static>() -> Option<T> {
    struct UseContextState<T2: Clone + PartialEq + 'static> {
        initialized: bool,
        context: Option<(T2, ContextHandle<T2>)>,
    }

    let scope = get_current_scope()
        .expect("No current Scope. `use_context` can only be called inside function components");

    use_hook(
        move || UseContextState {
            initialized: false,
            context: None,
        },
        |state: &mut UseContextState<T>, updater| {
            if !state.initialized {
                state.initialized = true;
                let callback = move |ctx: T| {
                    updater.callback(|state: &mut UseContextState<T>| {
                        if let Some(context) = &mut state.context {
                            context.0 = ctx;
                        }
                        true
                    });
                };
                state.context = scope.context::<T>(callback.into());
            }

            Some(state.context.as_ref()?.0.clone())
        },
        |state| {
            state.context = None;
        },
    )
}