Until now, you've learned of the two typical methods of transferring data between components.
To pass data from a parent to a child component by attributes.
Emit events from a child component to inform a parent to run a workflow.
However, toddle's context feature allows a parent component to provide information to any component within its hierarchy, regardless of depth, bypassing the need for explicitly passing attributes. This removes a lot of repetitive work and will save you time. Similarly, it can expose workflows directly to children, so they can be called without the need for events.
The problem with passing a state
Passing a state as attributes becomes unwieldy in complex component trees, especially when multiple components require the same data. This often leads to "attribute drilling," passing information through various levels to reach the necessary components. Sometimes, it is used for components that don't even use the value to reach the specific components that need it. It's wasteful and should be avoided.
toddle's context feature solves this. It allows data to be directly shared with the required components in the tree, eliminating the need for attribute drilling. This "teleports" the data exactly where it's needed, simplifying data flow in complex component structures.
Step 1: Define Context
You can share two types of values as context to the nested tree: Formulas and workflows. With these values, you can share any data from your components. Formulas provide information, while workflows allow sub-components to communicate back up the tree.
For instance, to share a variable, you could create a new component formula that returns its value or a new computed value based on the variable. Remember to check "Expose in context".
If you also want sub-components to be able to modify this variable, implementing a workflow that utilizes the Set variable action to update it is straightforward.
Step 2: Use the Context
We are now ready to consume context from another component! 🙌 First, let us create a new component.
Next, we need to indicate that we want to subscribe to the context from the parent component. You do this in the "Contexts" section of your component's data panel.
Step 3: Use the Components
You can now use both the context-providing and the consumer components in your app. Ensure that your consuming component lives underneath the component tree of your context-providing component. If there are multiple providers, it will use the nearest one.
☝️Hint: According to the Single responsibility principle, we often use a pattern of context together with slots. The components only job is to provide context, and by using slots you can add it to any other component's children, by wrapping them inside the default slot.
Pros and Cons: Attributes & Events vs. Context
While Context in toddle is powerful for sharing data across many components, it's not always preferable over attributes+events for a few reasons:
Overuse Leads to Unmaintainable Components: Using Context excessively can make the data flow in your application harder to track, leading to less maintainable and understandable components. With attributes and events, the data flow is explicit and easy to follow.
Component Reusability: Components relying on a lot of Context can become less reusable. These components are tightly coupled with the Context they consume, making them unable to work if their context providers do not exist or have unexpected data. On the other hand, some complex components can become simple plug-and-play as long as their context is available.
Scalability Concerns: Managing a large global state with Context can become challenging as applications grow. State is effectively complexity, and complexity is the enemy of large apps. Context can both create or remove complexity, depending on the problem you're trying to solve.
In summary, while Context is useful for avoiding attribute drilling and sharing global data, it's important to use it judiciously. Attributes and events are often more suitable for direct parent-child communication, ensuring component independence, maintainability, and clearer data flow.