A personal context API wrapper for state management driven by the desire to learn more and experiment.
A simple wrapper for the Context API.
# Using NPM
npm install --save context-model
# Using Yarn
yarn add context-model
First define the Context Model:
// ANCHOR React
import { useState } from 'react';
// ANCHOR Model
import createStore from 'context-model';
/**
* ANCHOR Counter
* Creating a Counter context model using `createStore`.
* `contextModel` will return an object with `Provider` and `useStore` methods.
*/
const Counter = createStore(({ initialCount }) => {
const [count, setCount] = useState(initialCount ?? 0);
const increment = () => {
setCount((count) => count + 1);
}
const decrement = () => {
setCount((count) => count - 1);
}
const reset = () => {
setCount(0);
}
return {
count,
increment,
decrement,
reset,
}
},
);
export default Counter;
First define the Context Model:
// ANCHOR React
import { useState } from 'react';
// ANCHOR Model
import createStore from 'context-model';
/**
* ANCHOR IProps
* This will be the basis for the type of
* the context model's provider component
*
* The following example makes the initialCount
* prop optional
*/
interface IProps {
initialCount?: number;
}
/**
* ANCHOR Counter
* Creating a Counter context model using `createStore`.
* `contextModel` will return an object with `Provider` and `useStore` methods.
*/
const Counter = createStore(({ initialCount }: IProps) => {
const [count, setCount] = React.useState(initialCount ?? 0);
const increment = () => {
setCount((count) => count + 1);
}
const decrement = () => {
setCount((count) => count - 1);
}
const reset = () => {
setCount(0);
}
return {
count,
increment,
decrement,
reset,
}
},
);
export default Counter;
To allow the consumption of the context, add the Provider
to the component tree.
function App() {
return (
<Counter.Provider>
<Count />
<Controls />
</Counter.Provider>
);
}
To consume the context, you may use the useStore
hook. This returns the state object.
function Count() {
const { count } = Counter.useStore();
return <h1>{count}</h1>
}
function Controls() {
const { increment, decrement, reset } = Counter.useStore();
return (
<>
<button onClick={increment}>increment</button>
<button onClick={decrement}>decrement</button>
<button onClick={reset}>reset</button>
</>
);
}
useStore
can be use in the following ways:
// Getting the state object
const store = Counter.useStore();
// Deconstructing the state object
const { count, reset } = Counter.useStore();
To consume and transform the context, you may use the useSelector
hook. This takes in a selector function as its props then returns a value based on the given function.
// Providing a callback function that transforms the store value
const count = Counter.useSelector((state) => state.count + 10);
const [incrementedCount, decrementedCount] = Counter.useSelector((state) => [
state.count + 1,
state.count - 1,
]);
// Or you can do it like this 👀
function addTen(number) {
return number + 10;
}
const count = Counter.useSelector((state) => addTen(state.number));
// This callback function is also useful for picking specific value(s) from the store like so:
const count = Counter.useSelector((state) => state.count);
For better experience in debugging using the Dev Tools, you may add the displayName
of the context model. In this case I named the model as Counter
:
const Counter = createStore((state) => state, {
displayName: 'Counter',
});
Being new to React, I wanted to learn more about state management. Thus I decided to try my hand on it by creating a simple wrapper for the Context API. The react-scoped-model inspired the structure of this wrapper, namely the usage of useSelector
and Provider
.
A list of all things Context API. You might want to check it out if you're also curious to learn more 👀.