Skip to content
Scalable state using React Hooks & Context
Branch: master
Clone or download
Latest commit ba6423b Apr 18, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.vscode feat: Hooks (#51) Nov 21, 2018
examples chore: Upgrade constate dependency on examples Apr 14, 2019
logo Initial commit Mar 31, 2018
src chore: Update README.md Apr 14, 2019
test feat: Return a hook from the createContainer method (#78) Apr 14, 2019
.editorconfig Initial commit Mar 31, 2018
.eslintignore
.eslintrc chore: Add eslint rules and remove useless babel-core Jan 28, 2019
.gitignore Add examples (#20) May 26, 2018
.npmrc chore: Upgrade dependencies Mar 5, 2019
.travis.yml feat: Hooks (#51) Nov 21, 2018
CHANGELOG.md chore: Update CHANGELOG.md Apr 14, 2019
LICENSE Initial commit Mar 31, 2018
MIGRATING_FROM_V0_TO_V1.md chore: Update docs Dec 15, 2018
README.md Update README.md Apr 18, 2019
babel.config.js feat: Hooks (#51) Nov 21, 2018
jest.config.js chore: Remove useless jest setup file Jan 28, 2019
package.json chore(release): 1.1.1 Apr 14, 2019
rollup.config.js chore: Upgrade rollup Jan 2, 2019
tsconfig.json feat: `createContainer` (#58) Dec 14, 2018
yarn.lock chore: Upgrade dependencies Apr 14, 2019

README.md

constate logo

Constate

NPM version NPM downloads Size Dependencies Build Status Coverage Status

Write local state using React Hooks and lift it up to React Context only when needed with minimum effort.


🕹 CodeSandbox demos 🕹
Counter I18n Theming TypeScript Wizard Form

import React, { useState } from "react";
import createUseContext from "constate";

// 1️⃣ Create a custom hook as usual
function useCounter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(prevCount => prevCount + 1);
  return { count, increment };
}

// 2️⃣ Wrap your hook with the createUseContext factory
const useCounterContext = createUseContext(useCounter);

function Button() {
  // 3️⃣ Use context instead of custom hook
  const { increment } = useCounterContext()
  return <button onClick={increment}>+</button>;
}

function Count() {
  // 4️⃣ Use context in other components
  const { count } = useCounterContext()
  return <span>{count}</span>;
}

function App() {
  // 5️⃣ Wrap your components with Provider
  return (
    <useCounterContext.Provider>
      <Count />
      <Button />
    </useCounterContext.Provider>
  );
}

Installation

npm:

npm i constate

Yarn:

yarn add constate

API

createUseContext(useValue[, createMemoInputs])

Constate exports a single factory method called createUseContext. It receives two arguments: useValue and createMemoInputs (optional). And returns a wrapped hook that can now read state from the Context. The hook also has two static properties: Provider and Context.

useValue

It's any custom hook:

import { useState } from "react";
import createUseContext from "constate";

const useCounterContext = createUseContext(() => {
  const [count] = useState(0);
  return count;
});

console.log(useCounterContext); // React Hook
console.log(useCounterContext.Provider); // React Provider
console.log(useCounterContext.Context); // React Context (if needed)

You can receive arguments in the custom hook function. They will be populated with <Provider />:

const useCounterContext = createUseContext(({ initialCount = 0 }) => {
  const [count] = useState(initialCount);
  return count;
});

function App() {
  return (
    <useCounterContext.Provider initialCount={10}>
      ...
    </useCounterContext.Provider>
  );
}

The API of the containerized hook returns the same value(s) as the original, as long as it is a descendant of the Provider:

function Counter() {
  const count = useCounterContext();
  console.log(count); // 10
}

createMemoInputs

Optionally, you can pass in a function that receives the value returned by useValue and returns an array of inputs. When any input changes, value gets re-evaluated, triggering a re-render on all consumers (components calling useContext()).

If createMemoInputs is undefined, it'll be re-evaluated everytime Provider renders:

// re-render consumers only when value.count changes
const useCounterContext = createUseContext(useCounter, value => [value.count]);

function useCounter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  return { count, increment };
}

This works similarly to the inputs parameter in React.useEffect and other React built-in hooks. In fact, Constate passes it to React.useMemo inputs internally.

You can also achieve the same behavior within the custom hook. This is an equivalent implementation:

import { useMemo } from "react";

const useCounterContext = createUseContext(() => {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  // same as passing `value => [value.count]` to `createMemoInputs` parameter
  return useMemo(() => ({ count, increment }), [count]);
});

Contributing

If you find a bug, please create an issue providing instructions to reproduce it. It's always very appreciable if you find the time to fix it. In this case, please submit a PR.

If you're a beginner, it'll be a pleasure to help you contribute. You can start by reading the beginner's guide to contributing to a GitHub project.

When working on this codebase, please use yarn. Run yarn examples:start to run examples.

License

MIT © Diego Haz

You can’t perform that action at this time.