Lightweight, flexible, and predictable state management for modern React applications.
Blac is a state management library designed to bring simplicity and power to your React projects. It draws inspiration from established patterns like Redux and the Bloc pattern, while offering a more intuitive API and minimizing boilerplate. At its core, Blac focuses on type safety (with first-class TypeScript support) and an excellent developer experience.
It consists of two main packages:
@blac/core
: The foundational library providing the core Blac/Bloc logic, instance management, and plugin system.@blac/react
: The React integration layer, offering hooks and utilities to seamlessly connect Blac with your React components.
An overview of the Blac pattern, its core concepts, and how it simplifies state management in React.
- Simple API: Intuitive and easy to learn, minimizing boilerplate.
- Smart Instance Management: Automatic creation, sharing (default for non-isolated Blocs using class name or provided ID), and disposal of
Bloc
instances. SupportskeepAlive
for persistent state and isolated instances (viastatic isolated = true
or unique IDs). - TypeScript First: Strong typing for robust applications and excellent developer experience.
- Extensible: Add custom functionality through a built-in plugin system or create addons for
Bloc
s (like thePersist
addon for storage). - Performance: Lightweight core and efficient updates.
- Flexible Architecture: Adapts to various project needs, from simple components to complex applications.
To get started with Blac in your React project, install the @blac/react
package. This package includes @blac/core
.
# Using pnpm (recommended for this monorepo)
pnpm add @blac/react
# Or using npm
npm install @blac/react
# Or using yarn
yarn add @blac/react
Here's a taste of how to use Blac with a simple counter:
// 1. Define your Cubit (e.g., in src/cubits/CounterCubit.ts)
import { Cubit } from '@blac/core';
interface CounterState {
count: number;
}
// CounterCubit manages the counter's state
export class CounterCubit extends Cubit<CounterState> {
constructor() {
// Initialize the state with a count of 0
super({ count: 0 });
}
// Define methods to update the state
// Remember: methods must be arrow functions to bind 'this' correctly!
increment = () => this.emit({ count: this.state.count + 1 });
decrement = () => this.emit({ count: this.state.count - 1 });
reset = () => this.emit({ count: 0 });
}
// 2. Use the Cubit in your React component (e.g., in src/components/CounterDisplay.tsx)
import { useBloc } from '@blac/react';
import { CounterCubit } from '../cubits/CounterCubit'; // Adjust path as needed
function CounterDisplay() {
// Connect your component to the CounterCubit.
// useBloc returns a tuple: [currentState, cubitInstance]
const [state, counterCubit] = useBloc(CounterCubit);
return (
<>
<h1>Count: {state.count}</h1> {/* Access state directly */}
<button onClick={counterCubit.increment}>Increment</button>
<button onClick={counterCubit.decrement}>Decrement</button>
<button onClick={counterCubit.reset}>Reset</button>
</>
);
}
export default CounterDisplay;
BlocBase
: The foundational abstract class for state containers.Cubit<State>
: A simpler state container that exposes methods (similar to Zustand) to directlyemit
orpatch
new states. The Quick Start example above uses aCubit
.Bloc<State, Action>
: A more advanced state container that processesAction
s (events) through areducer
function (similar to Redux reducers) to produce newState
. This is useful for more complex state logic where transitions are event-driven and require more structure.useBloc
Hook: The primary React hook from@blac/react
to connect components toBloc
orCubit
instances, providing the current state and the instance itself. It efficiently re-renders components when relevant state properties change.- Instance Management: Blac's central
Blac
instance intelligently manages yourBloc
s/Cubit
s. By default, non-isolated Blocs are shared (keyed by class name or a custom ID). Blocs can be marked asstatic isolated = true
or given unique IDs for component-specific state, and can be configured withstatic keepAlive = true
to persist in memory.
- 💡 Simple & Intuitive API: Get started quickly with familiar concepts and less boilerplate.
- 🧠 Smart Instance Management by the central
Blac
class:- Automatic creation and disposal of
Bloc
instances based on usage. - Non-isolated
Bloc
s are shared by default (keyed by class name or custom ID). - Isolated
Bloc
s (markedstatic isolated = true
or given a unique ID viauseBloc
options) for component-specific or distinct states. Bloc
s are kept alive as long as they have active listeners/consumers, or if explicitly marked withstatic keepAlive = true
.
- Automatic creation and disposal of
- 🔒 TypeScript First: Full type safety out-of-the-box, enabling robust applications and great autocompletion.
- 🧩 Extensible via Plugins & Addons:
- Plugins: Extend Blac's core functionality by hooking into lifecycle events (e.g., for logging).
- Addons: Enhance individual
Bloc
capabilities (e.g., state persistence with thePersist
addon).
- 🚀 Performance Focused:
- Minimal dependencies for a small bundle size.
- Efficient state updates and re-renders in React.
- 🧱 Flexible Architecture: Adapts to various React project structures and complexities.
For comprehensive documentation, including advanced usage, API details, and guides, please refer to:
- Local Docs: Run
pnpm run dev:docs
in theapps/docs
directory and open the provided local URL. - Online Docs: (TODO: Add link to the deployed documentation site here if available)
Contributions are highly welcome! Whether it's bug fixes, feature enhancements, or documentation improvements, please feel free to:
- Fork the repository.
- Create your feature branch (
git checkout -b feature/AmazingFeature
). - Commit your changes (
git commit -m 'Add some AmazingFeature'
). - Push to the branch (
git push origin feature/AmazingFeature
). - Open a Pull Request.
Please ensure your code adheres to the project's linting and formatting standards.
Blac is MIT licensed.