All Projects → kyleshevlin → react-generate-context

kyleshevlin / react-generate-context

Licence: MIT license
A helper function for reducing React Context boilerplate

Programming Languages

typescript
32286 projects
HTML
75241 projects

Projects that are alternatives of or similar to react-generate-context

no-redux
⚛️ 🎣 Experimenting with using hooks and context instead of Redux
Stars: ✭ 79 (+229.17%)
Mutual labels:  hooks, context
laravel-react-spa
A Laravel-React SPA starter project template.
Stars: ✭ 94 (+291.67%)
Mutual labels:  hooks, context
Web3 React
🧰 A simple, maximally extensible, dependency minimized framework for building modern Ethereum dApps
Stars: ✭ 788 (+3183.33%)
Mutual labels:  hooks, context
use-app-state
🌏 useAppState() hook. that global version of setState() built on Context.
Stars: ✭ 65 (+170.83%)
Mutual labels:  hooks, context
Use Global Context
A new way to use “useContext” better
Stars: ✭ 34 (+41.67%)
Mutual labels:  hooks, context
Hunch
Hunch provides functions like: All, First, Retry, Waterfall etc., that makes asynchronous flow control more intuitive.
Stars: ✭ 94 (+291.67%)
Mutual labels:  context
react-context-tabs
Flexible tabs for React
Stars: ✭ 31 (+29.17%)
Mutual labels:  context
context
A proof of concept implementation of scoped context
Stars: ✭ 16 (-33.33%)
Mutual labels:  context
Dapper.AmbientContext
Ambient context implementation for Dapper.NET
Stars: ✭ 31 (+29.17%)
Mutual labels:  context
react-ui-hooks
🧩Simple repository of React hooks for building UI components
Stars: ✭ 20 (-16.67%)
Mutual labels:  hooks
react-context-global-store
A simple global store based on React context
Stars: ✭ 22 (-8.33%)
Mutual labels:  context
kubeswitch
visually select kubernetes context/namespace from tree
Stars: ✭ 15 (-37.5%)
Mutual labels:  context
rocket-pipes
Powerful pipes for TypeScript, that chain Promise and ADT for you 🚌 -> ⛰️ -> 🚠 -> 🏂 -> 🚀
Stars: ✭ 18 (-25%)
Mutual labels:  context
joincontext
Join contexts like never before!
Stars: ✭ 19 (-20.83%)
Mutual labels:  context
react-zap
⚡ Zap props from one React component to another, using React new context API and your existing higher-order components ⚡
Stars: ✭ 17 (-29.17%)
Mutual labels:  context
ctx
🍭Ctx (Context) 是一个服务模块化上下文框架。
Stars: ✭ 38 (+58.33%)
Mutual labels:  context
elcontext
Context-based actions for Emacs
Stars: ✭ 18 (-25%)
Mutual labels:  context
xMenuTools
Extended context menu tools for Windows
Stars: ✭ 56 (+133.33%)
Mutual labels:  context
react-combine-contexts
🚀Use multiple React Contexts without pain.
Stars: ✭ 23 (-4.17%)
Mutual labels:  context
wc-context
Context for Web Components
Stars: ✭ 26 (+8.33%)
Mutual labels:  context

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 = [
  0,
  {
    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]
}

/**
 * The defaultValue to 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 the 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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].