Solid.js is already pretty powerful, but even so, there are things it cannot do out of the box. Here's where the community comes in and provides packages to enhance your development experience: solid-primitives.
As the author of a few of those packages, I want to delve into our collection to present you a few gems that might end up being helpful to you. Here's the first one
@solid-primitives/context
If you, like me, make extensive use of context, you probably already know the term "context hell" or can imagine what it looks like:
<FirstContext.Provider>
<SecondContext.Provider>
<ThirdContext.Provider>
<AndSoOnContext.Provider>...
Wouldn't it be better if you could just compose contexts together like this:
[FirstContext, SecondContext, ThirdContext, AndSoOnContext, ...]
MultiProvider
to the rescue!
Long story short, it allows you to compose contexts as an array:
import { MultiProvider } from '@solid-primitives/context';
<MultiProvider values={
[FirstContext, SecondContext, ThirdContext]}>
...
</MultiProvider>
Even better, you can compose contexts depending on configuration and also add values by using a tuple [Provider, values]
:
const providers = [
featureOne && FirstContext,
SecondContext,
featureThree && ThirdContext,
[AndSoOnContext.Provider, providerProps]
];
return <MultiProvider values={providers}>...</MultiProvider>;
It doesn't matter if you add the context or the context.Provider, MultiProvider will automatically detect what you gave it and handle it correctly. How's that for convenience? This functionality comes at only ~213bytes increase of bundle sizeยน and is especially useful for feature toggles and A/B testing.
We're not done yet, though. This package has another export:
createContextProvider
Since we're using a lot of context, we want to reduce the boilerplate that goes into the creation of each of them. Imagine we have a Counter component that encapsulates its state in a context:
import { useCounter } from './CounterContext';
function ContextUsingCounter() {
const { count, increment } = useCounter();
return <button onClick={increment}>{count()}</button>;
}
The context for such a component would probably look like this:
import { createContext, createSignal, useContext } from 'solid-js';
import { type JSX } from 'solid-js/web';
const CounterContext = createContext({});
export function CounterProvider(props: { initial?: number, children?: JSX.Elements } = {}) {
const [count, setCount] = createSignal(props.initial ?? 0);
const increment = () => setCount(count() + 1);
return <CounterContext.Provider values={{ count, increment }}>
{props.children}
</CounterContext.Provider>;
}
export function useCounter() {
return useContext(CounterContext)
}
Instead, we can leverage createContextProvider
to wrap things for us, resulting in more concise code:
import { createContextProvider } from '@solid-primitives/context';
export const [MyContextProvider, useCounter] =
createContextProvider((props: { initial: number }) => {
const [count, setCount] = createSignal(props.initial);
const increment = () => setCount(count() + 1);
return { count, increment };
});
Since this primitive adds ~151bytes to your bundle sizeยน, using it 2-3 times should already decrease your bundle size.
Final words
I hope you'll find something useful in our collection. However, if you are missing something or have problems using our primitives, don't hesitate to ask for it, either here, or - if you want a fast response - in the #solid-primitives
channel of the Solid.js discord.
ยน: this is the minified/gzipped size of the bundled primitive alone. Depending on your own package, there might be a slight difference in the actual increase of your bundle size, as code that is repeated often gzips better.
Top comments (1)
Looks very useful, thanks for sharing!