Skip to content

Commit

Permalink
feat: <Provider/> component to fix context shadowing (closes #2038) (
Browse files Browse the repository at this point in the history
  • Loading branch information
gbj authored Nov 20, 2023
1 parent 4518d3c commit cb6267a
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 1 deletion.
2 changes: 2 additions & 0 deletions leptos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,11 @@ mod error_boundary;
pub use error_boundary::*;
mod animated_show;
mod for_loop;
mod provider;
mod show;
pub use animated_show::*;
pub use for_loop::*;
pub use provider::*;
#[cfg(feature = "experimental-islands")]
pub use serde;
#[cfg(feature = "experimental-islands")]
Expand Down
40 changes: 40 additions & 0 deletions leptos/src/provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use leptos::*;

#[component]
/// Uses the context API to [`provide_context`] to its children and descendants,
/// without overwriting any contexts of the same type in its own reactive scope.
///
/// This prevents issues related to “context shadowing.”
///
/// ```rust
/// # use leptos::*;
/// #[component]
/// pub fn App() -> impl IntoView {
/// // each Provider will only provide the value to its children
/// view! {
/// <Provider value=1u8>
/// // correctly gets 1 from context
/// {use_context::<u8>().unwrap_or(0)}
/// </Provider>
/// <Provider value=2u8>
/// // correctly gets 2 from context
/// {use_context::<u8>().unwrap_or(0)}
/// </Provider>
/// // does not find any u8 in context
/// {use_context::<u8>().unwrap_or(0)}
/// }
/// }
/// ```
pub fn Provider<T>(
/// The value to be provided via context.
value: T,
children: Children,
) -> impl IntoView
where
T: Clone + 'static,
{
run_as_child(move || {
provide_context(value);
children()
})
}
24 changes: 23 additions & 1 deletion leptos_reactive/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,29 @@ use std::any::{Any, TypeId};
/// that was provided in `<Parent/>`, meaning that the second `<Child/>` receives the context
/// from its sibling instead.
///
/// This can be solved by introducing some additional reactivity. In this case, it’s simplest
/// ### Solution
///
/// If you are using the full Leptos framework, you can use the [`Provider`](leptos::Provider)
/// component to solve this issue.
///
/// ```rust
/// # use leptos::*;
/// #[component]
/// fn Child() -> impl IntoView {
/// let context = expect_context::<&'static str>();
/// // creates a new reactive node, which means the context will
/// // only be provided to its children, not modified in the parent
/// view! {
/// <Provider value="child_context">
/// <div>{format!("child (context: {context})")}</div>
/// </Provider>
/// }
/// }
/// ```
///
/// ### Alternate Solution
///
/// This can also be solved by introducing some additional reactivity. In this case, it’s simplest
/// to simply make the body of `<Child/>` a function, which means it will be wrapped in a
/// new reactive node when rendered:
/// ```rust
Expand Down

0 comments on commit cb6267a

Please sign in to comment.