npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-combo-provider

v1.0.8

Published

Tiny (around 0.7Kb minzipped) no dependencies utility function to quickly generate (with 1 call) Contexts, Providers and hooks that share the memory efficiently

Downloads

16

Readme

npm bundle size

What

Tiny (around 0.7Kb minzipped) no dependencies utility function to quickly generate (with 1 call) Contexts, Providers and hooks that share the memory efficiently.

Most common scenario is a Store implementation. With Context Provider it's attached to your DOM wherever you want (local or global - you decide).

Why

Defining a Store Context, Provider and hooks (and also Types) requires some boilerplate. We might want the "writing" components to be independent of rendering caused by data changes - then "data" and "API" have to be provided with separate Contexts and hooks (see an awesome article on this topic). Means even more boilerplate.

And if we (for example) have some model and want every field to be changed and rendered independently - it would mean that every field should have its own Context, Provider and hook. The code is pretty standard, but it's a lot of code - see examples below 👇

This simple utility saves you dozens (or even hundreds) of code lines to implement your efficient Contexts/Stores.

How does it work

Every created hook is linked to its dedicated Context under the hood for rendering optimization.

All the generated contexts stack within a single generated ComboProvider wrapper component for convenience. It's easy to forget this fact - that's why generated Provider has a special automatic "...ComboProvider" suffix.

How to use it

You provide (function params)

  • provider base name (for the Provider component generation)
  • hooks (slices/contexts) names, as many as you want (each hook will be rendered independently due to its individual Context under the hood)
  • their shared memory scope (all the data that hooks are going to use, just like a regular hook)
    • with custom parameters for more control (will be Provider props, optional)
    • return an object that maps every hook to the data you need

You get (function returns)

  • controllable Provider component
  • your generated hooks
  • 🎉 with all the names and types ready to be exported right away

Example 1

Simplest Context providing a value (let's provide the useState result: value + setter, no rendering separation)

import { makeComboProviderAndHooks } from 'react-combo-provider';
import { useState } from 'react';

// Provider component + hooks generated with all the types, use code completion and export them right away
export const { CountStoreComboProvider, useCount } = makeComboProviderAndHooks(
  'countStore', // provider base name (<x>ComboProvider component will be generated, with corresponding displayName)
  ['count'], // a list of hooks/contexts to generate. Every item generates a <x>Context layer for your Provider and a use<X> hook
  () => {
    // provider's props can be defined as an argument here
    return {
      // key = Context name (see array above); value = what hook returns (via its own context)
      count: useState(0), // value for the useCount hook
    };
  },
);

If we omit comments - the code is really compact.

import React, {
  createContext,
  type Dispatch,
  type PropsWithChildren,
  type ReactElement,
  type SetStateAction,
  useContext,
  useState,
} from 'react';

// define a Context type, makeComboProviderAndHooks infers your types automatically
type Count = [number, Dispatch<SetStateAction<number>>];

// create the Context instance with empty state for Provider check, makeComboProviderAndHooks does it for you
const CountContext = createContext<Count | null>(null);
// displayName - makeComboProviderAndHooks generates it too
CountContext.displayName = 'CountContext';

// Context hook with user-friendly Provider check - makeComboProviderAndHooks generates the same for you
export function useCount(): Count {
  const context = useContext(CountContext);
  if (!context) {
    throw new Error('useCount must be within CountStoreProvider');
  }
  return context;
}

// Context Provider component with displayName. Also generated by makeComboProviderAndHooks
export function CountStoreProvider({ children }: PropsWithChildren): ReactElement {
  return <CountContext.Provider value={useState(0)}>{children}</CountContext.Provider>;
}

As we can see, actually useful code (useState(0) and a few titles) needs a lot of boilerplate around.

Example 2

Same example, but now we want writers to be independent of changed data - 2 Contexts, 2 hooks (useCount, useSetCount)

import { makeComboProviderAndHooks } from 'react-combo-provider';
import { useState } from 'react';

export const { CountStoreComboProvider, useCount, useSetCount } = makeComboProviderAndHooks(
  'countStore',
  [
    'count',
    'setCount',
  ], // 2 contexts/hooks
  () => {
    const [
      count,
      setCount,
    ] = useState(0);

    return {
      // key = Context name (see array above); value = what hook returns (with its own context)
      count, // value for the useCount hook
      setCount, // value for the useSetCount hook
    };
  },
);

We simply added 1 item to the definition array + 1 property to the returned mapping.

Let's see how equivalent code grows up.

import React, {
  createContext,
  type Dispatch,
  type PropsWithChildren,
  type ReactElement,
  type SetStateAction,
  useContext,
  useState,
} from 'react';

type CountData = number;
type CountApi = Dispatch<SetStateAction<number>>;

const CountDataContext = createContext<CountData | null>(null);
CountDataContext.displayName = 'CountDataContext';

const CountApiContext = createContext<CountApi | null>(null);
CountApiContext.displayName = 'CountApiContext';

export function useCountData(): CountData {
  const context = useContext(CountDataContext);
  if (context == null) {
    throw new Error('useCountData must be within CountStoreProvider');
  }
  return context;
}

export function useCountApi(): CountApi {
  const context = useContext(CountApiContext);
  if (!context) {
    throw new Error('useCountApi must be within CountStoreProvider');
  }
  return context;
}

export function CountStoreProvider({ children }: PropsWithChildren): ReactElement {
  const [
    count,
    setCount,
  ] = useState(0);

  return (
    <CountApiContext.Provider value={setCount}>
      <CountDataContext.Provider value={count}>{children}</CountDataContext.Provider>
    </CountApiContext.Provider>
  );
}

Pretend now that we want more fields in our store. And many other stores in our app.

With ComboProvider Context (and Store) creation and rendering optimization have never been easier.