DEV Community

Alex Lohr
Alex Lohr

Posted on

๐Ÿ’Ž of solid-primitives, part 1: context

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>...
Enter fullscreen mode Exit fullscreen mode

Wouldn't it be better if you could just compose contexts together like this:

[FirstContext, SecondContext, ThirdContext, AndSoOnContext, ...]
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>;
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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 };
  });
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
andyjessop profile image
Andy Jessop

Looks very useful, thanks for sharing!