Business vs Application Logic: how to separate and test your ReactJS code

Antony Leme
2 min readNov 13, 2024

--

A common concept among React developers that I disagree with is creating hooks to separate business logic from the rest of the application.

In my view, this happens because there’s a misunderstanding of what “business logic” actually is.

In the context of React, it’s easy to clarify what is not business logic:

  • React hooks (useEffect, useState, etc.)
  • State management (Redux, Zustand, React Query…)
  • Event handlers (handleSubmit, handleClick…)
  • UI (JSX)

The rule is simple: anything directly related to React itself is not business logic.

With the exception of the UI, these items are actually application logic, which serve the sole purpose of defining implementation details and interactions that will orchestrate our interface.

For application logic, the best choice is indeed to create custom hooks to separate responsibilities between application logic and UI, avoiding mixing these logics with the interface (which is already a separate layer).

However, business logic, in my view, in the functional context of the front end, mainly consists of:

  • Conditionals (ifs)
  • Calculations
  • Data extraction, formatting, and validation
  • API and SDK calls

For these, I think the best choice is to create pure functions that receive a parameter and return a result. This way, they work independently and have a clear input and output. We can then create unit tests that call these functions directly, making them more resilient and ensuring that we’re testing what really matters: the outcome of isolated logic.

Following this approach, the core of your application (business logic) remains isolated from React, allowing for the possibility in the future to switch libraries/frameworks or even to adopt a new version of the interface without having to rewrite all the business rules. You’ll only need to couple the existing business logic to your new application.

But when it comes to application logic (custom hooks), it doesn’t make sense to test them directly. Since custom hooks are implementation details and their role is to orchestrate user flows, it’s better to test what the user actually sees: the interface.

For example, instead of testing if handleOpenModal is changing the isOpen state value to true, you test what matters: whether the modal is visible on the screen.

This way, you stop testing implementation details and start testing what truly matters: application behavior. This ensures that tests will only fail if the behavior changes, regardless of changes in implementation.

Mastering the skill of choosing what to test and how to test is essential for creating sustainable tests in your application.

Otherwise, fragile tests lose developers’ trust and become dead weight in the codebase: failing tests will stop meaning anything, and a culture of commenting out tests just to get code to ship will emerge.

--

--

No responses yet