Socket
Socket
Sign inDemoInstall

constate

Package Overview
Dependencies
0
Maintainers
1
Versions
40
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    constate

Yet another React state management library that lets you work with local state and scale up to global state with ease


Version published
Weekly downloads
304K
increased by4.77%
Maintainers
1
Install size
56.4 kB
Created
Weekly downloads
 

Readme

Source

constate logo

Constate

NPM version NPM downloads Gzip size Dependencies Build Status Coverage Status

~1 kB React state management library that lets you write contextual state as if it were local state using React Hooks and React Context.


🕹 CodeSandbox demos 🕹
CounterThemingI18n

import React from "react";
import { Provider, useContextState } from "constate";

// 1. Create a custom hook
function useCounter(key) {
  // 2. Replace React.useState(0) by useContextState(key, 0)
  const [count, setCount] = useContextState(key, 0);
  const increment = () => setCount(count + 1);
  return { count, increment };
}

function Count() {
  // 3. Consume the custom hook as usual
  const { count } = useCounter("counter1");
  return <span>{count}</span>
}

function IncrementButton() {
  // 4. Consume the same key in other components
  const { increment } = useCounter("counter1");
  return <button onClick={increment}>+</button>;
}

function App() {
  // 5. Wrap your app with Provider
  return (
    <Provider>
      <Count />
      <IncrementButton />
    </Provider>
  );
}

Table of Contents

Installation

npm:

npm i constate@next

Yarn:

yarn add constate@next

You'll need to install react@next and react-dom@next

Constate v1 is currently in early alpha. If you're looking for v0, see v0 docs or read the migration guide.


Provider

↑ Back to top

First, you should wrap your app (or the part using Constate) with Provider so as to access contextual state within hooks:

import React from "react";
import { Provider } from "constate";

function App() {
  return (
    <Provider devtools={process.env.NODE_ENV === "development"}>
      ...
    </Provider>
  );
}

Passing devtools prop to Provider will enable the redux-devtools-extension integration, if that's installed in your browser. With that, you can easily debug the state of your application.

Using Redux Devtools Extension


useContextState

↑ Back to top

useContextState has the same API as React.useState, except that it receives contextKey as the first argument. It can be either a string or the return value of useContextKey.

All useContextState calls with the same contextKey throughout components in the Provider tree will share the same state.

import { useContextState } from "constate";

function Component() {
  // accesses state.contextKey in context
  const [state, setState] = useContextState("contextKey", "initialValue");
  ...
}

If you pass null or undefined into the contextKey parameter, it'll work exactly like React.useState:

import { useContextState } from "constate";

function Component() {
  // same as React.useState("initialValue")
  const [state, setState] = useContextState(null, "initialValue");
  ...
}

This means you can create custom hooks that can be either contextual or local depending on the component using it:

import React from "react";
import { useContextState } from "constate";

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

function ContextualCounter() {
  const { count, increment } = useCounter("counter1");
  return <button onClick={increment}>{count}</button>;
}

function LocalCounter() {
  const { count, increment } = useCounter();
  return <button onClick={increment}>{count}</button>;
}

useContextReducer

↑ Back to top

Just like useContextState, useContextReducer works similarly to React.useReducer, but accepting a contextKey argument, which can be either a string or the return value of useContextKey:

import { useContextReducer } from "constate";

function reducer(state, action) {
  switch(action.type) {
    case "INCREMENT": return state + 1;
    case "DECREMENT": return state - 1;
    default: return state;
  }
}

function useCounter(key) {
  const [count, dispatch] = useContextReducer(key, reducer, 0);
  const increment = () => dispatch({ type: "INCREMENT" });
  const decrement = () => dispatch({ type: "DECREMENT" });
  return { count, increment, decrement };
}

function ContextualCounter() {
  const { count, increment } = useCounter("counter1");
  return <button onClick={increment}>{count}</button>;
}

useContextKey

↑ Back to top

Instead of passing strings to useContextState and useContextReducer, you can create a reference to the context key.

import { useContextKey } from "constate";

function Counter() {
  const key = useContextKey("counter1");
  const [count, setCount] = useContextState(key, 0);
  ...
}

It uses React.useRef underneath and is required when using useContextEffect.


useContextEffect

↑ Back to top

Constate provides all contextual versions of React.useEffect, such as useContextEffect and useContextLayoutEffect.

They receive contextKey as the first argument. Unless useContextState and useContextReducer, it's limited to the value returned by useContextKey. If contextKey is null or undefined, the hook will work exactly as the React one.

import { Provider, useContextKey, useContextEffect } from "constate";

let count = 0;

function useCounter(context) {
  // useContextKey is required for effects
  const key = useContextKey(context);
  useContextEffect(key, () => {
    count += 1;
  }, []);
}

function ContextualCounter1() {
  useCounter("counter1");
  ...
}

function ContextualCounter2() {
  useCounter("counter1");
  ...
}

function App() {
  return (
    <Provider>
      <ContextualCounter1 />
      <ContextualCounter2 />
    </Provider>
  );
}

In the example above, if we were using React.useEffect, count would be 2. With useContextEffect, it's 1.

useContextEffect ensures that the function will be called only once per contextKey no matter how many components are using it.


createContext

↑ Back to top

If you want to set a initial state for the whole context tree and/or want to create separate contexts, you can use createContext:

// MyContext.js
import { createContext } from "constate";

const {
  Provider,
  useContextKey,
  useContextState,
  useContextReducer,
  useContextEffect,
  useContextLayoutEffect
} = createContext({
  counter1: 0,
  posts: [
    { id: 1, title: "Hello World!" }
  ]
});

export {
  Provider,
  useContextKey,
  useContextState,
  useContextReducer,
  useContextEffect,
  useContextLayoutEffect
};
// App.js
import React from "react";
import { Provider, useContextState } from "./MyContext";

function Counter() {
  // no need for initial value, it has been set in context
  const [count, setCount] = useContextState("counter1");
  const increment = () => setCount(count + 1);
  return <button onClick={increment}>{count}</button>;
}

function App() {
  return (
    <Provider>
      <Counter />
    </Provider>
  );
}

When importing hooks directly from the constate package, you're, in fact, using a default context created by our index file.


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

Keywords

FAQs

Last updated on 03 Dec 2018

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc