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-generate-context

v4.0.1

Published

Factory function for React Contexts

Downloads

223,227

Readme

react-generate-context

React Context with less boilerplate.

Creating a new React Context involves a few steps. react-generate-context removes a couple of those steps.

The react-generate-context package is a single function, generateContext, that generates a React Context (in closure) and returns to you the Provider and custom hook to access it in one step. All you need to do is give it a function that creates and updates the value prop for your Context. Let's go through an example:

import generateContext from 'react-generate-context'

type Context = [
  number,
  {
    inc: () => void
    dec: () => void
  }
]

type Props = {
  startingCount: number
}

/**
 * `generateContext` receives a custom hook function that manages the `value`
 * passed to the Provider under the hood. The function takes any `props` passed
 * to the Provider
 */
const useGetCounterValue = ({ startingCount }: Props): Context => {
  const [state, setState] = React.useState(startingCount)
  const handlers = React.useMemo(
    () => ({
      inc: () => {
        setState(s => s + 1)
      },
      dec: () => {
        setState(s => s - 1)
      },
    }),
    []
  )

  return [state, handlers]
}

/**
 * An optional defaultValue can be passed to the underlying `createContext` function
 */
const defaultValue: Context = [
  0,
  {
    inc: () => {},
    dec: () => {},
  },
]

/**
 * generateContext returns a tuple of a Provider and a custom
 * hook to consume the context. Array destructuring allows you
 * to name the Provider and hook whatever you need to easily
 */
const [CounterProvider, useCounter] = generateContext<Context, Props>(
  useGetCounterValue,
  defaultValue
)

/**
 * We can consume that context in a component with the hook
 */
function Counter() {
  const [count, { inc, dec }] = useCounter()

  return (
    <div>
      {count}
      <div>
        <button onClick={inc}>+</button>
        <button onClick={dec}>-</button>
      </div>
    </div>
  )
}

/**
 * And use the generated Provider
 */
function App() {
  return (
    <CounterProvider startingCount={100}>
      <Counter />
    </CounterProvider>
  )
}

Installation

npm install react-generate-context

or

yarn add react-generate-context

API

const [MyProvider, useMyContext] = generateContext<Context, Props>(
  useGetContextValue,
  defaultValue
)

generateContext receives two arguments: useGetContextValue and an optional defaultValue for your Context.

useGetContextValue

type UseGetContextValue<Props, Context> = (props: Props) => Context

The useGetContextValue is a custom hook function that derives the value of your context. It is given any props passed to the Provider.

Example:

type Props = {
  startingCount: number
}

type Context = [
  number,
  {
    inc: () => void
    dec: () => void
  }
]

const useGetCounterValue = ({ startingCount }: Props): Context => {
  const [state, setState] = React.useState(startingCount)
  const handlers = React.useMemo(
    () => ({
      inc: () => {
        setState(s => s + 1)
      },
      dec: () => {
        setState(s => s - 1)
      },
    }),
    []
  )

  return [state, handlers]
}

defaultValue

defaultValue is the value utilized by the Context when a Consumer is rendered without a Provider as a parent. It is passed to React.createContext under the hood.

Why?

Reducing boilerplate aside, there's one other good reason to use a helper like generateContext when creating Contexts (or at least follow the pattern of its Provider).

The Provider returned to you does not allow you to put any components or elements in the same scope where the state change for the context is occurring. This prevents you from making a mistake that causes unnecessary rerendering. For example:

import React from 'react'
import SomeOtherFeature from './SomeOtherFeature'
import useManageValue from './useManageValue'

const defaultValue = {}

const MyContext = React.createContext(defaultValue)
const useMyContext = () => React.useContext(MyContext)

const MyProvider = ({ children }) => {
  const value = useManageValue()

  return (
    <MyContext.Provider value={value}>
      {children}
      <SomeOtherFeature />
    </MyContext.Provider>
  )
}

In this instance, because we have composed SomeOtherFeature in the same scope as where our state change for value occurs, no matter what you do to SomeOtherFeature, even if it doesn't consume useMyContext, it will be rerendered every time value changes.

The Provider returned to you by generateContext only allows you to use it with composition via children. It ensures that no mistake like the one above can be made now or in the future. Your Provider will work as well as it can. The onus is still on you to write a good custom hook to manage the value.