-
The problemOccasionally in my work developing components for Primer, I have had the need to access attributes of a component's grandparent from a grandchild, or at even more distant levels of the hierarchy. Traditionally I have accomplished this by including the necessary information in system arguments, but there are some problems with such an approach, namely:
Context in ReactIn the React world, one way of solving prop drilling problems like this is to use the function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Button>Click me</Button>
</ThemeContext.Provider>
);
}
function Button({children}) {
const theme = useContext(ThemeContext);
return <button className={`theme-${theme.value}`}>{children}</button>
} While this example only shows a single layer of nesting, ProposalI propose that ViewComponent offer a similar feature to React's class MyPage < ViewComponent::Base
def around_render
provide_context(:theme, { value: "dark" }) do
yield
end
end
end
class Button
def call
use_context(:theme) do |theme_context|
content_tag(:button, class: "theme-#{theme_context[:value]}") { content }
end
end
end The Template-level contextBecause <% provide_context(:theme, { value: "dark" }) do %>
<%= render(SubComponent.new) %>
<% end %> Class-level contextAlthough providing context from a template is cool, it only works inside component templates. For that reason, I also propose allowing <% AnyViewComponentClassOrMaybeApplicationComponent.provide_context(:theme, { value: "dark" }) do %>
<%= render(SubComponent.new) %>
<% end %> This would allow context to be provided and used from any Rails template, eg. view templates, partials, etc. ImplementationI believe the cleanest implementation would involve setting the context on each component instance and modifying the Another potential solution might be to make use of thread-local variables, eg: Proof of conceptI've worked up a proof of concept you can take a look at here: main...context Looking forward to hearing your feedback! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
I brought this proposal up at VC office hours this week. We agreed the context pattern I'm describing isn't unique to ViewComponent and could in fact be used in the broader Ruby ecosystem to set and consume block-scoped context values. My plan is:
Will update this discussion when I've got something worth sharing. |
Beta Was this translation helpful? Give feedback.
Alright, I put together a general-purpose context sharing gem I'm calling weft. You can see the code and README here: https://github.com/camertron/weft. It borrows some code I found when researching Rails' current attributes, and allows storing context in either thread- or fiber-local storage.
I'd love to hear any and all feedback 😄
Honestly the code is so minimal it almost feels like this concept would be better applied to codebases as a copy/paste-able snippet rather than a full-blown gem. Curious to hear what others think.