Top Related Projects
Recoil is an experimental state management library for React apps. It provides several capabilities that are difficult to achieve with React alone, while being compatible with the newest features of React.
🐻 Bear necessities for state management in React
A JS library for predictable global state management
Simple, scalable state management.
Quick Overview
Constate is a lightweight and flexible state management library for React that uses React Context API. It provides a simple and intuitive way to manage global state in React applications, without the need for complex state management solutions like Redux or MobX.
Pros
- Simplicity: Constate has a minimal API and is easy to understand and use, making it a great choice for small to medium-sized React projects.
- Flexibility: Constate allows you to create and manage state in a modular way, making it easy to scale your application as it grows.
- Performance: Constate uses the React Context API, which is optimized for performance and avoids unnecessary re-renders.
- No Boilerplate: Constate eliminates the need for boilerplate code that is often required in other state management solutions.
Cons
- Limited Ecosystem: Constate is a relatively new library, and the ecosystem of third-party tools and plugins is not as extensive as some other state management solutions.
- Lack of Tooling: Constate does not have the same level of tooling and debugging support as some other state management solutions, which can make it more difficult to work with in larger projects.
- Learning Curve: While Constate is relatively simple to use, developers who are new to the React Context API may still need to invest some time in learning how to use it effectively.
- Lack of Middleware: Constate does not provide built-in support for middleware, which can be useful for implementing features like logging, error handling, or performance optimization.
Code Examples
Here are a few examples of how to use Constate in a React application:
Creating a simple counter state:
import { createState } from 'constate';
const [useCounter, { increment, decrement }] = createState(() => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
Consuming the counter state in a component:
import { useCounter } from './counter';
function CounterComponent() {
const { count, increment, decrement } = useCounter();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
Combining multiple states:
import { createState } from 'constate';
const [useUserState, { setUser }] = createState(() => ({
user: null,
setUser: (user) => set({ user }),
}));
const [useThemeState, { setTheme }] = createState(() => ({
theme: 'light',
setTheme: (theme) => set({ theme }),
}));
const [useAppState] = createState(() => ({
...useUserState(),
...useThemeState(),
}));
Getting Started
To get started with Constate, you can follow these steps:
- Install the library using npm or yarn:
npm install constate
- Create a new state using the
createState
function:
import { createState } from 'constate';
const [useCounter, { increment, decrement }] = createState(() => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
- Use the state in your React components:
import { useCounter } from './counter';
function CounterComponent() {
const { count, increment, decrement } = useCounter();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
- Combine multiple states using the
createState
function:
import
Competitor Comparisons
Recoil is an experimental state management library for React apps. It provides several capabilities that are difficult to achieve with React alone, while being compatible with the newest features of React.
Pros of Recoil
- Offers a more flexible and scalable state management solution for complex React applications
- Provides built-in support for asynchronous data fetching and derived state
- Enables fine-grained updates and optimizations through atom-based state management
Cons of Recoil
- Steeper learning curve due to its unique concepts and API
- Requires more boilerplate code for setting up atoms and selectors
- Still in experimental phase, which may lead to API changes and potential instability
Code Comparison
Recoil:
import { atom, useRecoilState } from 'recoil';
const counterState = atom({
key: 'counterState',
default: 0,
});
function Counter() {
const [count, setCount] = useRecoilState(counterState);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
Constate:
import React from 'react';
import constate from 'constate';
const [CounterProvider, useCounter] = constate(() => {
const [count, setCount] = React.useState(0);
return { count, setCount };
});
function Counter() {
const { count, setCount } = useCounter();
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
🐻 Bear necessities for state management in React
Pros of zustand
- Simpler API with less boilerplate
- Built-in middleware support (e.g., persist, devtools)
- Can be used outside of React components
Cons of zustand
- Less type-safe compared to constate's TypeScript support
- Doesn't leverage React's Context API, which may be preferred in some cases
- Steeper learning curve for developers familiar with React's built-in state management
Code Comparison
constate:
const useCounter = createState(useState(0));
const [Counter, CounterProvider] = constate(useCounter);
function App() {
return (
<CounterProvider>
<Counter />
</CounterProvider>
);
}
zustand:
const useStore = create(set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 }))
}));
function Counter() {
const count = useStore(state => state.count);
const increment = useStore(state => state.increment);
return <button onClick={increment}>{count}</button>;
}
Both libraries aim to simplify state management in React applications, but they take different approaches. constate builds upon React's Context API, making it more familiar to React developers, while zustand provides a standalone state management solution with a focus on simplicity and flexibility.
A JS library for predictable global state management
Pros of Redux
- Robust ecosystem with extensive middleware and developer tools
- Predictable state management with a single source of truth
- Well-suited for large-scale applications with complex state logic
Cons of Redux
- Steeper learning curve and more boilerplate code
- Can be overkill for smaller applications or simpler state management needs
- Requires careful consideration of performance optimizations for large state trees
Code Comparison
Redux:
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
default:
return state;
}
};
Constate:
const useCounter = () => {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
return { count, increment };
};
Redux offers a more structured approach with actions and reducers, while Constate leverages React's built-in hooks for simpler state management. Redux is better suited for complex state logic, while Constate provides a more lightweight solution for component-level state management.
Simple, scalable state management.
Pros of MobX
- More powerful and feature-rich state management solution
- Supports complex scenarios with computed values and reactions
- Better performance for large-scale applications with many state updates
Cons of MobX
- Steeper learning curve due to its complexity and concepts
- Requires more boilerplate code for setup and configuration
- May be overkill for simple applications or components
Code Comparison
MobX:
import { makeAutoObservable } from "mobx";
class Timer {
secondsPassed = 0;
constructor() {
makeAutoObservable(this);
}
increase() {
this.secondsPassed += 1;
}
}
Constate:
import React from "react";
import constate from "constate";
const [CounterProvider, useCounter] = constate(() => {
const [count, setCount] = React.useState(0);
const increment = () => setCount(prevCount => prevCount + 1);
return { count, increment };
});
MobX offers a more robust solution for complex state management scenarios, while Constate provides a simpler, React-focused approach using hooks and context. MobX is better suited for large applications with intricate state interactions, whereas Constate is ideal for smaller projects or components that require lightweight state management.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Constate
Write local state using React Hooks and lift it up to React Context only when needed with minimum effort.
Counter | I18n | Theming | TypeScript | Wizard Form |
Basic example
import React, { useState } from "react";
import constate from "constate";
// 1ï¸â£ Create a custom hook as usual
function useCounter() {
const [count, setCount] = useState(0);
const increment = () => setCount(prevCount => prevCount + 1);
return { count, increment };
}
// 2ï¸â£ Wrap your hook with the constate factory
const [CounterProvider, useCounterContext] = constate(useCounter);
function Button() {
// 3ï¸â£ Use context instead of custom hook
const { increment } = useCounterContext();
return <button onClick={increment}>+</button>;
}
function Count() {
// 4ï¸â£ Use context in other components
const { count } = useCounterContext();
return <span>{count}</span>;
}
function App() {
// 5ï¸â£ Wrap your components with Provider
return (
<CounterProvider>
<Count />
<Button />
</CounterProvider>
);
}
Advanced example
import React, { useState, useCallback } from "react";
import constate from "constate";
// 1ï¸â£ Create a custom hook that receives props
function useCounter({ initialCount = 0 }) {
const [count, setCount] = useState(initialCount);
// 2ï¸â£ Wrap your updaters with useCallback or use dispatch from useReducer
const increment = useCallback(() => setCount(prev => prev + 1), []);
return { count, increment };
}
// 3ï¸â£ Wrap your hook with the constate factory splitting the values
const [CounterProvider, useCount, useIncrement] = constate(
useCounter,
value => value.count, // becomes useCount
value => value.increment // becomes useIncrement
);
function Button() {
// 4ï¸â£ Use the updater context that will never trigger a re-render
const increment = useIncrement();
return <button onClick={increment}>+</button>;
}
function Count() {
// 5ï¸â£ Use the state context in other components
const count = useCount();
return <span>{count}</span>;
}
function App() {
// 6ï¸â£ Wrap your components with Provider passing props to your hook
return (
<CounterProvider initialCount={10}>
<Count />
<Button />
</CounterProvider>
);
}
Installation
npm:
npm i constate
Yarn:
yarn add constate
API
constate(useValue[, ...selectors])
Constate exports a single factory method. As parameters, it receives useValue
and optional selector
functions. It returns a tuple of [Provider, ...hooks]
.
useValue
It's any custom hook:
import { useState } from "react";
import constate from "constate";
const [CountProvider, useCountContext] = constate(() => {
const [count] = useState(0);
return count;
});
You can receive props in the custom hook function. They will be populated with <Provider />
:
const [CountProvider, useCountContext] = constate(({ initialCount = 0 }) => {
const [count] = useState(initialCount);
return count;
});
function App() {
return (
<CountProvider initialCount={10}>
...
</CountProvider>
);
}
The API of the containerized hook returns the same value(s) as the original, as long as it is a descendant of the Provider:
function Count() {
const count = useCountContext();
console.log(count); // 10
}
selectors
Optionally, you can pass in one or more functions to split the custom hook value into multiple React Contexts. This is useful so you can avoid unnecessary re-renders on components that only depend on a part of the state.
A selector
function receives the value returned by useValue
and returns the value that will be held by that particular Context.
import React, { useState, useCallback } from "react";
import constate from "constate";
function useCounter() {
const [count, setCount] = useState(0);
// increment's reference identity will never change
const increment = useCallback(() => setCount(prev => prev + 1), []);
return { count, increment };
}
const [Provider, useCount, useIncrement] = constate(
useCounter,
value => value.count, // becomes useCount
value => value.increment // becomes useIncrement
);
function Button() {
// since increment never changes, this will never trigger a re-render
const increment = useIncrement();
return <button onClick={increment}>+</button>;
}
function Count() {
const count = useCount();
return <span>{count}</span>;
}
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
to run examples.
License
MIT © Diego Haz
Top Related Projects
Recoil is an experimental state management library for React apps. It provides several capabilities that are difficult to achieve with React alone, while being compatible with the newest features of React.
🐻 Bear necessities for state management in React
A JS library for predictable global state management
Simple, scalable state management.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot