1 kB React state management library that lets you write contextual state
as if it were local state, using React Hooks.
🎮 Play with CodeSandbox examples
Counter
import React from "react";
import { Provider, useContextState } from "constate";
function DecrementButton() {
const [count, setCount] = useContextState("counter1", 0);
const decrement = () => setCount(count - 1)
return <button onClick={decrement}>-</button>;
}
function IncrementButton() {
const [count, setCount] = useContextState("counter1", 0);
const increment = () => setCount(count + 1)
return <button onClick={increment}>+</button>;
}
function Count() {
const [count] = useContextState("counter1", 0);
return <span>{count}</span>
}
function App() {
return (
<Provider>
<DecrementButton />
<Count />
<IncrementButton />
</Provider>
);
}
Table of Contents
Installation
npm i constate@next
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.
useContextState
↑ Back to top
useContextState
has the same API as React.useState
, except that it receives contextKey
as the first argument.
import { useContextState } from "constate";
function Component() {
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() {
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(context) {
const [count, setCount] = useContextState(context, 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:
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(context) {
const [count, dispatch] = useContextReducer(context, 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>;
}
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
:
import { createContext } from "constate";
const { Provider, useContextState, useContextReducer } = createContext({
counter1: 0,
posts: [
{ id: 1, title: "Hello World!" }
]
});
export { Provider, useContextState, useContextReducer };
import React from "react";
import { Provider, useContextState } from "./MyContext";
function Counter() {
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.
createContext
receives a second argument name
, which will be displayed in the Redux Devtools when using the devtools
prop on Provider
.
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