Skip to main content

Paranormal Reactivity πŸ‘»

Β· 6 min read
Fabio Fognani
random nerd

I'm afraid it's a little bit late for Halloween, but I do have a spooky update for you.

Our beloved PubSub-based reactive system has two new features. They're designed to eliminate manual boilerplate, guarantee data integrity (even in asynchronous scenarios) and optimize performance.

The new key functionalities are:

  • a method for making reactive properties: for a cleaner syntax and less boilerplate
  • a method for batching notifications (and its related decorator): for maximum efficiency and predictability of the PubSub mechanism

Here are the details.


1. Zero boilerplate: makeReactiveProperties​

Until now, making a property reactive required using decorators or calling this.notify('propName') every time a value changed. While this is the responsibility of a reactive layer implementing a PubSub pattern, I realize it can be tedious and repetitive.

The new feature makeReactiveProperties eliminates this friction, allowing you to write your class properties naturally, delegating the complex notification setup to the framework's core unless you have very specific needs that require manual calls to notify.

The Paradox of use-less-react: getting more from React by using less React

Β· 8 min read
Fabio Fognani
random nerd

Given that use-less-react deliberately employs an object-oriented philosophy distinct from React's functional-hook paradigm, it might seem that the two tools are in opposition, and that use-less-react wants to "fight" React. In reality, the opposite is true: use-less-react is designed to simplify and lighten React. Its core mandate is to free complex business logic from the constraints of UI-centric APIs. Specifically, Hooks.

By integrating classic OOP patterns for domain management, we gain immediate and important benefits:

  • True Dependency Injection (DI): solving the complex problem of service management that React handles poorly.
  • Intuitive testability: shifting complex state management and side-effects out of functional component lifecycles (where testing is often heavy and unintuitive) into pure, easily testable classes.
  • Elimination of boilerplate: eradicating common frustrations like Stale Closures and the overuse of useCallback / useMemo required merely to appease the dependency array linter.

Thus, by minimizing the use of React-specific API for your state, a paradox unfolds: by using less React, React can ultimately be used more. By adopting use-less-react's philosophy, our code not only gains in terms of scalability, but it also becomes accessible to a broader range of developers (including Node.js back-end developers), and it becomes viable for more sophisticated project types where the current limitations of Hooks severely restrict front-end expressiveness.

Your App Just Got an Undo Button: Adding the Memento Pattern to use-less-react

Β· 6 min read
Fabio Fognani
random nerd

I'm thrilled to announce that the Memento Pattern is now officially supported and deeply integrated into the core of use-less-react! (from v0.6.0)

Read the technical documentation here!

This means adding robust "Undo" and "Redo" functionality to your complex state managers has never been easier, allowing your users to step back in time with a single click.

Wait, What is the Memento Pattern?​

Imagine you're designing a complex graphics editor, a state-heavy spreadsheet, or even just a simple text input. Your user makes a mistake. They expect to hit Ctrl+Z (or Cmd+Z) and revert their action.

The Half-Truth of React: MVVM Contaminated

Β· 6 min read
Fabio Fognani
random nerd

React is often categorized as an MVVM (Model-View-ViewModel) framework. This classification is based on two core principles it perfectly enforces:

  1. Unidirectional Data Flow: State flows strictly from Model to View (M β†’ VM β†’ V). The View does not directly mutate the Model; it sends commands back to the ViewModel/Component.
  2. Conceptual Separation: The code conceptually separates the Logic (VM) from the Presentation (V).

However, this is only half the truth. The full truth is that React's design, particularly with Hooks, enforces a critical architectural fusion that undermines the pattern's primary benefitβ€”pure testability:

  • View (V): Your JSX output.
  • ViewModel (VM): The logic that exposes state and actions.

The fundamental issue is that Hooks mandate the fusion of the VM into the V. Your functional component contains the JSX, but it also becomes the seat of the ViewModel (via useState, useEffect, etc.). This fusion is the root of contamination: it forces business logic to become dependent on React's rendering APIs, effectively getting in the way of testability and killing separation of concerns.

Hooks Kill Architecture: The Price Of Sacrificing Classes

Β· 7 min read
Fabio Fognani
random nerd

The introduction of Hooks in React was presented as a revolution, freeing us from the complexity of classes and their lifecycle methods. We willingly accepted the trade-off: cleaner code, less boilerplate.

But in this process, we imposed a set of rules upon ourselves β€” often not fully grasped β€” that have effectively rendered entire categories of Design Patterns unused, and in some cases impossible.

We accepted the price of sacrificing classes, a core feature of the language we're using, and this is something I must admit I overlooked, back in the day. Now I'm asking myself how could that happen. The paradox is clear: the world's most popular UI library abruptly failed to support a core language feature, thereby crippling the expressive potential available to engineers. How could the front-end community think this was a good idea, from an architectural standpoint?

By the way, this is not a debate about "classes vs functions": it is a warning about what we lost at a structural level.

Implementing the State Pattern for safe Auth Flow Management with use-less-react

Β· 10 min read
Fabio Fognani
random nerd

Introduction: The Inevitable Complexity of User Flows​

Imagine you're tasked with implementing a standard authentication flow in a React application. The requirement seems simple:

  1. The app must first check the session (look for a stored token).
  2. If no session is found, redirect to the login page.
  3. If the session is valid, transition to the authenticated state, granting access to core features.

Most React developers immediately reach for a large, central custom hook / context to manage this logic. They will write something like this:

// File: use-auth.ts

function useAuth() {
const [status, setStatus] = useState<'checking' | 'login' | 'authenticated'>('checking');
const [user, setUser] = useState(null);

useEffect(() => {
// Initial session check logic here
const session = getSession();
if (session.user) {
setStatus('authenticated');
setUser(session.user);
} else {
setsStatus('login');
}
}, []);

const login = async (email: string, password: string) => {
const session = await loginApi(email, password);
if (session.user) {
setStatus('authenticated');
setUser(session.user);
}
};

return { status, login, user };
};

The Inevitable Feature Creep​

Then, reality hits. A new requirement lands on your desk: the user must not only be authenticated but must also complete their profile with additional data like address, company ID, etc., before accessing the main app.

This means ripping into the heart of your flow logic, modifying all tests for the central hook, and introducing nested conditionals.

You modify your hook, adding the new pending-profile state. After many delicate changes, you arrive at what you believe is a stable solution.

But then, life happens again. A new, critical security requirement arrives: You must integrate a mandatory 2-Factor Authentication (2FA) step between login/signup and the profile completion stage.

This is a true nightmare. It forces you to re-engineer the same block of code again. The logic for transition, validation, and conditional routing is now becoming complex and fragile:

// File: use-auth.ts

const login = async (email: string, password: string) => {
const session = await loginApi(data);
if (session.user) {
setUser(session.user)
}
};

useEffect(() => {
if (!is2FASet(user)) {
setStatus('2fa-required')
} else if (!isProfileComplete(user)) {
setStatus('pending-profile');
} else {
setStatus('authenticated');
}
}, [user]);

This monolithic approach has failed even in our very simplified example. We are constantly violating the Open/Closed Principle (OCP): modifying existing, working code instead of just extending it.

The Architectural Question​

Are we truly following the right architectural path for managing complex, evolving application behavior?

The answer is no. With use-less-react, you can manage this exact problem using a powerful object-oriented design pattern: the State Pattern, implemented as a Finite State Machine (FSM). This approach uses the power of Object-Oriented Programming (OOP) to encapsulate behavior, making your logic robust, extensible, and easy to test.

Architectural Guardrails: How Clean Separation of Concerns Dramatically Simplifies PR Reviews

Β· 8 min read
Fabio Fognani
random nerd

Introduction: The High Cost of Unconstrained Complexity​

In software development, the true cost of a feature is measured not just by the time it takes to write, but by the time it takes to debug, change, and β€” last but not least β€” review. When logic is unconstrained, a simple feature often turns into a complex pull request (PR) that demands deep, time-consuming investigation.

This post contrasts two architectural approaches to demonstrate how Separation of Concerns, enforced by a class-based Domain Model (like in use-less-react), dramatically simplifies the PR review process.


Scenario 1: Hooks-based implementation​

Imagine a PR submitted by a junior developer for a new feature. The core logic is housed in a single component file, putting together data and logics from different hooks.

The Component-Centric Trap: Why Domain Logic Needs Its Own Lifecycle

Β· 5 min read
Fabio Fognani
random nerd

Introduction​

The prevalence of React Hooks has anchored application state and logic firmly within the concept of Component. While this simplifies local UI state, it creates a fundamental architectural problem for complex applications: the subordination of core business logic to the React Component Lifecycle.

Logic packaged in a custom hook is intrinsically tied to where it is executed β€” it only "lives" as long as the component that uses it is mounted, and its execution is dependent on the component's render cycle.

This is the core argument for elevating state and logic into independent, vanilla Domain Entities.


1. Subordination to the Component Lifecycle​

A custom hook, by definition, must adhere to the Rules of Hooks. This dependency means that the lifecycle of the data and the logic it contains is entirely governed by the useEffect and useLayoutEffect calls within its composition.

Ensuring Type Safety: Integrating Zod for Secure State Hydration

Β· 4 min read
Fabio Fognani
random nerd

Introduction: The Challenge of Hydration​

In modern web applications, State Hydration is the process of restoring serialised application state from an external source (like a server-side render payload, a persistent layer, or a network cache) into a live JavaScript object. This is a critical and highly vulnerable step.

Since the source data is external and therefore untrusted, directly assigning it to a live object instance can lead to fatal runtime type errors if the data structure is corrupted or outdated.

This is where the combination of the Object-Oriented Design of our Domain Model and a robust Runtime Validation Library like Zod provides a secure, quick, and elegant solution.

The Boilerplate Tax πŸ’Έ

Β· 6 min read
Fabio Fognani
random nerd

How use-less-react finally fixes React's boilerplate tax and makes clean architecture practical.​

You're a good developer. You believe in clean architecture. You've heard about separating your app into two "worlds":

  1. the World of Presentation (the UI): Your React components. The "Primary Adapter" that translates user clicks and keyboard tapping into application events.
  2. the World of Logic (the Core): Your business rules and application state. The pure TypeScript functions and classes that don't know or care about UI details.

You start building a new feature. Let's say, a simple dropdown menu.

"This is just UI state," you think. "It's simple. I'll just use useState."

function MyMenu() {
const [isOpen, setIsOpen] = useState(false); // Local. Easy.

return (
<div>
<button onClick={() => setIsOpen(o => !o)}>Toggle</button>
{isOpen && <div>Menu Content</div>}
</div>
);
}

This is clean & pragmatic. You're keeping state as local as possible until a different necessity arises.

Then, a new requirement lands on your desk.

"We're building a tutorial wizard, and it needs to be able to open that menu automatically to show the user where to click."

...and suddenly, you're not so sure about what's "local" and what's not.