Context
Edit this pageContext provides a way to pass data through the component tree without having to pass props down manually at every level.
When to use context
When you have a large component tree that requires state to be shared, context can be used. Context can be employed to avoid prop drilling, which is the practice of passing props through intermediate elements without using them directly.
If you only want to avoid passing some props through a few levels levels, component composition is often a simpler solution than context.
Signals are often a simplest solution since they can be imported directly into the components that need them.
Context is designed to share data that can be considered "global", or information that is regularly accessed by many components in your application or component tree. It offers a way to access values more efficiently instead of passing props through intermediate elements or importing them directly into components.
Creating context
Context is created using the createContext
function.
This function has a Provider
property that wraps the component tree you want to provide context to.
Providing context to children
To pass a value to the Provider
, you use the value
prop which can take in any value, including signals.
Once a value is passed to the Provider
, it is available to all components that are descendants of the Provider
.
When passing a single value, it can be directly passed to the value
prop:
However, if you need to access multiple values, they must be passed as an object literal, or using double curly braces ({{ }}
):
Consuming context
Once the values are available to all the components in the context's component tree, they can be accessed using the useContext
utility.
This utility takes in the context object and returns the value(s) passed to the Provider
:
When you are passing multiple values into the Provider
, you can destructure the context object to access the values you need.
This provides a readable way to access the values you need without having to access the entire context object:
Customizing context utilities
When an application contains multiple context objects, it can be difficult to keep track of which context object is being used. To solve this issue, you can create a custom utilities to create a more readable way to access the context values.
For example, when wrapping a component tree, you may want to create a custom Provider
component that can be used to wrap the component tree.
This also provides you with the option of re-using the Provider
component in other parts of your application, if needed.
Now if you had to access the Provider in different areas of your application, you can simply import the CounterProvider
component and wrap the component tree:
Similarly, you can create a custom utility to access the context values.
Instead of importing useContext
and passing in the context object on each component that you're using it in, creating a customized utility can make it easier to access the values you need:
The useCounter()
utility in this example can now be imported into any component that needs to access the context values:
Updating context values
Signals offer a way to synchronize and manage data shared across your components using context.
You can pass a signal directly to the value
prop of the Provider
component, and any changes to the signal will be reflected in all components that consume the context.
This offers a way to manage state across your components without having to pass props through intermediate elements.
Debugging with context
createContext
takes in an optional default value and it is possible it can return undefined
if not provided.
When working with TypeScript, this can introduce type issues that make it difficult to determine why your component is not rendering as expected.
To solve this issue, a default value can be specified when creating a context object, or errors can be handled manually through the use of a custom useMyContext
utility: