Replies: 3 comments
-
Considering that Riot.js is just 6kb and it doesn't use any virtual DOM under the hood, I see 2 possible solutions to solve this issue:
<page-a>
<menu context={context}/>
<sidebar context={context}/>
<footer context={context}/>
</page-a> + these components could be easier to test since the context can be provided as prop - you need to handle manually the context prop on each component
// in this case I am using @riotjs/observable but you can use any reactive lib
export const sidebarContext = observable({
isOpen: false,
toggle(isOpen) {
this.isOpen = isOpen
this.trigger('update:is-open', isOpen)
}
}) <sidebar>
<script>
import {sidebarContext} from './sidebar-context'
export default {
// watch the sidebar context
}
</script>
</sidebar> + elegant api and flow control - your components are harder to test since they depend on side effects coming from the outside |
Beta Was this translation helpful? Give feedback.
-
1 is definitely out of the question - I'm explicitly looking for a better pattern than props. 2 is interesting. Could work well. Will try it out. What about making something similar a built-in feature of Riot? I share the desire to keep Riot small (I remember the 1kb days), but at some point 7kb or even 8kb is worth it if there's substantial value that comes with it. I think something like a built-context API would bring that level of value and should be discussed by the Riot community. |
Beta Was this translation helpful? Give feedback.
-
Tried Observable and it's not right for my needs. Also, a simple event pattern like that could be done with namespaced events and the native event API: // sidebar.riot
onBeforeMount() {
window.addEventListener('sidebar:toggle', this.toggle)
},
toggle(e) {
console.log(e.detail)
}
// other-component.riot
closeSidebar() {
window.dispatchEvent(new CustomEvent('sidebar:toggle', { detail: false }));
} So I tried experimenting with Proxies. This is a quick solution much closer to what I need and it works: // create-context.js
// Utility function for creating a shared context.
export function createContext(data = {}) {
// The list of components subscribed to this context.
const components = [];
// Seal context data so components cannot add or delete properties.
Object.seal(data);
// Context Proxy object.
// Context is empty by default, but the parent component should provide default data when it creates the context.
// The handler calls each subscribed component's update method whenever context is modified.
const context = new Proxy(data, {
set(contextData, property, value) {
contextData[property] = value;
components.forEach(component => component.update());
return true;
}
});
// Components call this function to get the context and subscribe to its changes.
return function subscribe(component) {
components.push(component);
return context;
}
}
// home-component.riot
import createContext from 'create-context.js';
// Create a context. Child components will import this.
export const homeContext = createContext({foo: true});
onBeforeMount() {
// Components subscribe to their own context. They can also subscribe to other contexts.
this.homeContext = homeContext(this);
}
// deeply-nested-child-component.riot
import homeContext from 'home-component.riot';
onBeforeMount() {
// Child subscribes to ancestor context.
this.homeContext = homeContext(this);
this.homeContext.foo = false; // All components subscribed to homeContext will update.
} That's pretty close to what I'm looking for - simple and direct way for a parent component to share a reactive state with child components - but I'd love for this to be built-in. Riot could do the context creation automatically when it finds a In comparison, React's Context API is designed for this use case, but it requires a lot of code and set up and is pretty clunky imo. Riot needs to solve for this too and it could do it Riot style with a super simple and clean API. I don't believe it would increase the size of Riot in any negative way and would add a big missing feature. |
Beta Was this translation helpful? Give feedback.
-
I find prop-drilling to be one of the worst design patterns, especially passing around event handlers. I hold a grudge toward React for making such a disastrous idea mainstream.
Fortunately, there is an escape hatch in React with Context+Reducer, which in typical React fashion is verbose and unintuitive, but an escape hatch nonetheless. Has Riot explored some sort of shared context feature to solve the prop-drilling problem?
Primary use case
I have a page (a Riot component) and in it there are subpages and components. Some of these share common bits of data. Rather than passing this data down and around all over through many layers of components with props (aka prop-drilling), often never actually used by intermediate components, I would instead love to be able to define a context at the page level (or any other arbitrary level) which makes this context directly available to all children of the parent component which defines the context. Something like:
Beta Was this translation helpful? Give feedback.
All reactions