Package Exports
- constate
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (constate) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
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] = React.useState(0);
const [count, setCount] = useContextState("counter1", 0);
const decrement = () => setCount(count - 1)
return <button onClick={decrement}>-</button>;
}
function IncrementButton() {
// const [count, setCount] = React.useState(0);
const [count, setCount] = useContextState("counter1", 0);
const increment = () => setCount(count + 1)
return <button onClick={increment}>+</button>;
}
function Count() {
// const [count] = React.useState(0);
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
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
useContextState
has the same API as React.useState
, except that it receives contextKey
as the first argument.
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(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
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>;
}
useContextEffect
Constate provides all contextual versions of React.useEffect
, such as useContextEffect
, useContextMutationEffect
and useContextLayoutEffect
.
They receive contextKey
as the first argument. If contextKey
is null
or undefined
, the hook will work exactly as the React one.
import { Provider, useContextEffect } from "constate";
let count = 0;
function useCounter(context) {
useContextEffect(context, () => {
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
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,
useContextState,
useContextReducer,
useContextEffect,
useContextMutationEffect,
useContextLayoutEffect
} = createContext({
counter1: 0,
posts: [
{ id: 1, title: "Hello World!" }
]
});
export {
Provider,
useContextState,
useContextReducer,
useContextEffect,
useContextMutationEffect,
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.
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