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
useStateanduseReducer. - Optimally split context to prevent unnecessary renders.
useSelectorfunction.- 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-contextsor using yarn.
yarn add react-hook-contextsUsage
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 functionscreateUseReducer 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.