reca
v2.4.3
Published
ReCA - React Clean Architecture state manager
Readme
ReCA - React Clean Architecture state manager
Created at the intersection of Functional style and OOP technologies. It is based on the simplicity of the functional style of the view, enriched with OOP technologies for writing business logic. Perfect for beginner developers and complex enterprise applications
Features
- Microstores - calculations state of components don't affect to other components, small CPU usage for update states,
- Direct Functions Call - don't need heavy CPU utilization for search function in reducer, just call the function directly,
- No Boilerplate - write only business code without those debt,
- Dependency Injection - override any part of your application for unit test or other customer,
- Microfrontend - perfect support microfrontends out the box without any boilerplates,
- Simple Data Flow - don't need search functions call chain for debug your reducers,
- Code Organization - structures the code easily even for large enterprise applications,
- Extra Small Size - only 1kb of minified code.
Comparison with Other Libraries
| Feature | ReCA | Zustand | MobX | Redux | |---------|------|----------|------|-------| | Bundle Size | ~1KB | ~1KB | ~16KB | ~8KB | | Boilerplate | Minimal | Minimal | Medium | Heavy | | Learning Curve | Easy | Easy | Medium | Steep | | TypeScript | Built-in | Good | Good | Good | | Performance | Excellent | Excellent | Excellent | Good | | Dependency Injection | ✅ Built-in | ❌ Manual | ❌ Manual | ❌ Manual | | Clean Architecture | ✅ Native | ❌ Limited | ⚠️ Requires setup | ⚠️ Requires setup | | Microstores | ✅ Yes | ✅ Yes | ✅ Yes | ❌ Monostore | | SSR Support | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | | Middleware | Via DI | ✅ Yes | ❌ Limited | ✅ Yes | | Async Actions | ✅ Native | ✅ Native | ✅ Native | ⚠️ Requires thunk/saga |
Why Choose ReCA?
- Smallest footprint - Only 1KB minified, same as Zustand but with more features
- Zero boilerplate - No actions, reducers, or dispatchers needed
- Enterprise-ready - Built-in DI and Clean Architecture support
- Developer-friendly - Simple API, easy to learn and use
- Flexible - Choose between AutoStore (automatic) or Store (manual control)
- Type-safe - Full TypeScript support out of the box
Installation
Using npm
npm install reca reflect-metadataUsing yarn
yarn add reca reflect-metadataUsing pnpm
pnpm add reca reflect-metadataSetup
After installation, import reflect-metadata at the entry point of your application (e.g., index.tsx or main.tsx):
import "reflect-metadata";
import React from "react";
import ReactDOM from "react-dom/client";
import { App } from "./App";
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);Note: The
reflect-metadatapackage is required for dependency injection functionality. Make sure to import it before any other imports in your application entry point.
Examples
Quick Start - Counter Example
A simple example to get you started with ReCA:
// counter.store.ts
import { AutoStore } from "reca";
export class CounterStore extends AutoStore {
public count: number = 0;
public increment(): void {
this.count++;
}
public decrement(): void {
this.count--;
}
}
// Counter.tsx
import { useStore } from "reca";
import { CounterStore } from "./stores/counter.store";
export const Counter = () => {
const store = useStore(CounterStore);
return (
<div>
<h1>Count: {store.count}</h1>
<button onClick={store.increment}>+1</button>
<button onClick={store.decrement}>-1</button>
</div>
);
};ToDo Example
Create your Store by inheriting from AutoStore, and use it in a component via useStore hook.
// todo.store.ts
import {AutoStore} from "reca";
import type {FormEvent} from "react";
export class ToDoStore extends AutoStore {
public currentTodo: string = "";
public todos: string[] = [];
public handleAddTodo (): void {
this.todos.push(this.currentTodo);
}
public handleDeleteTodo (index: number): void {
this.todos.splice(index, 1);
}
public handleCurrentEdit (event: FormEvent<HTMLInputElement>): void {
this.currentTodo = event.currentTarget.value;
}
}
// todo.component.ts
import {useStore} from "reca";
import {ToDoStore} from "../stores/todo.store";
export const ToDoComponent = (): JSX.Element => {
const store = useStore(ToDoStore);
return (
<div className="todos">
<div className="todos-list">
{
store.todos.map((todo, index) => (
<div className="todo">
{todo}
<button
className="todo-delete"
onClick={() => store.handleDeleteTodo(index)}
type="button"
>
X
</button>
</div>
))
}
</div>
<div className="todos-input">
<input
onInput={store.handleCurrentEdit}
value={store.currentTodo}
/>
<button
onClick={store.handleAddTodo}
type="button"
>
add
</button>
</div>
</div>
);
};Example low-level Store
Also, if you need uncompromising performance, you can use the low-level Store. But you will need to start redrawing manually using the this.redraw() method. Also you must pass arrow function to all used HTMLElement events, such as onClick.
// todo.store.ts
import {Store} from "reca";
import type {FormEvent} from "react";
export class ToDoStore extends Store {
public currentTodo: string = "";
public todos: string[] = [];
public handleAddTodo (): void {
this.todos.push(this.currentTodo);
this.redraw();
}
public handleDeleteTodo (index: number): void {
this.todos.splice(index, 1);
this.redraw();
}
public handleCurrentEdit (event: FormEvent<HTMLInputElement>): void {
this.currentTodo = event.currentTarget.value;
this.redraw();
}
}
// todo.component.ts
import {useStore} from "reca";
import {ToDoStore} from "../stores/todo.store";
export const ToDoComponent = (): JSX.Element => {
const store = useStore(ToDoStore);
return (
<div className="todos">
...
<div className="todos-input">
<input
onInput={() => store.handleCurrentEdit()}
value={store.currentTodo}
/>
<button
onClick={() => store.handleAddTodo()}
type="button"
>
add
</button>
</div>
</div>
);
};Advanced Example - Dependency Injection for Enterprise Applications
This example demonstrates how to build scalable enterprise applications using ReCA with Dependency Injection. It shows the simplicity of business logic organization following Clean Architecture principles.
The example includes:
- Service Layer - encapsulates business logic and external API calls
- Model Layer - defines data structures
- Store Layer - manages state and coordinates services
- Component Layer - pure view logic
This architecture makes your code:
- Testable - easily mock services for unit tests
- Maintainable - clear separation of concerns
- Scalable - add new features without modifying existing code
- Flexible - swap implementations through DI (e.g., Repository, Provider, Logger)
// SpaceXCompanyInfo.ts
export class SpaceXCompanyInfo {
public name: string = "";
public founder: string = "";
public employees: number = 0;
public applyData (json: object): this {
Object.assign(this, json);
return this;
}
}
// SpaceXService.ts
import {reflection} from "first-di";
import {SpaceXCompanyInfo} from "../models/SpaceXCompanyInfo";
@reflection
export class SpaceXService {
public async getCompanyInfo (): Promise<SpaceXCompanyInfo> {
const response = await fetch("https://api.spacexdata.com/v3/info");
const json: unknown = await response.json();
// ... and manies manies lines of logics
if (typeof json === "object" && json !== null) {
return new SpaceXCompanyInfo().applyData(json);
}
throw new Error("SpaceXService.getCompanyInfo: response object is not json");
}
}
// SpaceXStore.ts
import {reflection} from "first-di";
import {AutoStore} from "reca";
import {SpaceXCompanyInfo} from "../models/SpaceXCompanyInfo.js";
import {SpaceXService} from "../services/SpaceXService.js";
@reflection
export class SpaceXStore extends AutoStore {
public companyInfo: SpaceXCompanyInfo = new SpaceXCompanyInfo();
public constructor (
private readonly spaceXService: SpaceXService,
// private readonly logger: Logger
) {
super();
}
public activate (): void {
this.fetchCompanyInfo();
}
private async fetchCompanyInfo (): Promise<void> {
try {
this.companyInfo = await this.spaceXService.getCompanyInfo();
} catch (error) {
// Process exceptions, ex: this.logger.error(error.message);
}
}
}
// SpaceXComponent.tsx
import {useStore} from "reca";
import {SpaceXStore} from "../stores/SpaceXStore.js";
export const TestStoreComponent = (): JSX.Element => {
const store = useStore(SpaceXStore);
return (
<div>
<p>
Company:
{" "}
{store.companyInfo.name}
</p>
<p>
Founder:
{" "}
{store.companyInfo.founder}
</p>
</div>
);
};
Documentation and Resources
Documentation
- Wiki - Comprehensive guides, tutorials, and API reference
- API Documentation - Detailed API documentation for all features
Community and Support
- Discord Server - Join our community for real-time help and discussions
- GitHub Discussions - Ask questions and share ideas
- GitHub Issues - Report bugs or request features
Contributing
We welcome contributions! See our:
- Contributing Guide - Learn how to contribute to the project
- Code of Conduct - Our community guidelines
- Security Policy - How to report security vulnerabilities
License
ReCA is MIT licensed.
