react-hook-contexts v0.2.0
react-hook-contexts
React-hook-contexts is a new way to use useContext
better.
Features
- Easy state management with
useState
anduseReducer
. - Optimally split context to prevent unnecessary renders.
useSelector
function.- Support for SSR.
Why
The context API
allows you to create a simple store in combination with hooks such as useState
and useReducer
.
However, it can lead to unnecessary renders if you don't split the context with proper granularity. It also doesn't have a feature like redux's useSelector. That means you have to memo. Please see the solutions.
On the other hand, while redux is appropriate for managing large amounts of state, I don't think it's necessary for small projects to adopt such a large library.
This library is intended to avoid the implementation costs of redux and also to prevent unnecessary renders, which is a problem with the context API.
Installation
You can install the package from npm.
npm install react-hook-contexts
or using yarn.
yarn add react-hook-contexts
Usage
General
import React from "react";
import { createUseStateContexts } from "react-hook-contexts";
// You can add global state here. easy !!
const [store, ContextProviders] = createUseStateContexts({
counter: 0,
message: "",
app: {
name: "react-hook-contexts",
description: "A easy global state management library",
},
});
const App = () => {
return (
<ContextProviders>
<AppName />
<Counter />
<CounterButton />
</ContextProviders>
);
// To prevent unnecessary renders,
// the context is automatically split in the virtual DOM as follows
//
// <CounterStateContext>
// <CounterDispatchContext>
// <MessageStateContext>
// <MessageDispatchContext>
// <AppStateContext>
// <AppDispatchContext>
// <AppName />
// <Counter />
// <CounterButton />
// </AppDispatchContext>
// </AppStateContext>
// </MessageDispatchContext>
// </MessageStateContext>
// </CounterDispatchContext>
// </CounterStateContext>
};
const Counter = () => {
// You can get the state value of the context as follows
const counter = store.counter.state();
return <p>counter: {counter}</p>;
};
const CounterButton = () => {
// It only gets the dispatch; separating the state and the dispatch prevents extra renders.
const setCounter = store.counter.dispatch();
return (
<>
<button onClick={() => setCounter((counter) => counter + 1)}>+ 1</button>
<button onClick={() => setCounter((counter) => counter - 1)}>- 1</button>
</>
);
};
const AppName = () => {
// Like the redux useSelector API, you can retrieve only the state you need.
// And there are no unnecessary renders.
const name = store.app.state((app) => app.name);
return <p>App: {name}</p>;
};
export default App;
createUseStateContexts
API
createUseStateContexts
is generated by executing the values of the passed object as useState
arguments, each of which is divided with appropriate granularity as a value of context
.
import React from "react";
import { createUseStateContexts } from "react-hook-contexts";
const [store, ContextProviders] = createUseStateContexts({
counter: 0,
message: "",
app: {
name: "react-hook-contexts",
description: "A easy global state management library",
}
});
You can use it as follows.
const counter = store.counter.state();
// 0
const message = store.message.state();
// ""
const appState = store.app.state();
// {
// name: "react-hook-contexts",
// description: "A easy global state management library",
// }
const appName = store.app.state(app => app.name)
// "react-hook-contexts"
const counterDispatch = store.counter.dispatch()
const messageDispatch = store.message.dispatch()
const appDispatch = store.app.dispatch()
// Each of the dispatch functions
createUseReducer
API
createUseReducerContexts
is createUseStateContexts
as well as generated by executing the values of the passed object as useReducer
arguments, each of which is divided with appropriate granularity as a value of context
.
import React from "react";
import { createUseReducerContexts } from "react-hook-contexts";
const initialState = {
count: 0,
};
const ActionType = {
INCREMENT: "INCREMENT",
DECREMENT: "DECREMENT",
} as const;
type CounterAction = {
type: keyof typeof ActionType;
};
const reducer: React.Reducer<typeof initialState, CounterAction> = (
state,
action
) => {
switch (action.type) {
case ActionType.INCREMENT: {
return {
count: state.count + 1,
};
}
case ActionType.DECREMENT: {
return {
count: state.count - 1,
};
}
default: {
return state;
}
}
};
export const [store, ContextProviders] = createUseReducerContexts({
counter: {
reducer,
initialState,
}
});
The usage is the same as useCreateReducerContexts
API.
Examples
CreateUseStateContexts API example
This is an example of a counter app that uses the createUseStateContexts
API.
Notice that each time you increase/decrease the count, only the render of the Counter comport is running. (No unnecessary renders are happening.)
CreateUseReducerContexts API example
Similar to the example above, this is an example of a counter app using the createUseReducerContexts
API.