Separation of Concerns in React

Separation of Concerns in React

What is it?

A human behavior we generally separate everything to make it easy to work with. Like in kitchen we usually put spoons, forks, knife, plate, bowl, cooker, and cup in a group. We can also see the grouping in various places like supermarket, pharmacy. Same thing goes with software development. Separating makes it clearer to identify problems and attention on solutions.

Kitchen sink photo next to a photo of a supermarket aisle.

Separation of concerns is a programming principle that inspires grouping functionality in ways that decrease overlap. Basically separating a computer program into different segments such that each section addresses a separate concern. For example separate the code that contacts the database from the business logic on the backend

In React, we solved this issue by creating container components that contain all the logic, which would then pass down the data via props to the presentational component.

Why Needed 

  • The strategies required to ensure that each component only concerns itself with a single set of cohesive responsibilities often result in natural extensibility points.
  • Lack of repetition and singularity of purpose of the individual components render the system easy to manage.
  • The system becomes more stable resulting of the increased maintainability.
  • Applying this to react means that our component will look cleaner and more ordered. We won’t need to scroll past the wall of logic afore editing the UI.
  • Testing is more accessible as well: we can test the logic separately from the UI if we want to. 
  • The decoupling which results from requiring components to focus on a single purpose leads to components which are more easily reused in other systems, or different contexts within the same system.

Decouple logic with React hooks

We first create a custom hook. Below example it calculates the exponential value of the base number and the exponent: 

export const ExponentCalculator = () => {
    const [base, setBase] = useState(4);
    const [exponent, setExponent] = useState(4);
    const result = (base ** exponent).toFixed(2);
    //on base change  const handleBaseChange = (e) => {
      e.preventDefault();
      setBase(e.target.value);
    };
    //on exponent change  const handleExponentChange = (e) => {
      e.preventDefault();
      setExponent(e.target.value);
    };
   
    return (
      <div className="blue-wrapper">
        <input type="number" className="base" onChange={handleBaseChange} placeholder="Base" value={base}/>
        <input type="number" className="exponent" onChange={handleExponentChange} placeholder="Exp." value={exponent}/>
        <h1 className="result">{result}</h1>
      </div>
    );
  };Code language: JavaScript (javascript)

This may appearance fine already, but imagine if there’s more logic in here.

1st step, we will move the logic to a custom hook then call it inside our component.

const useExponentCalculator = () => {
    const [base, setBase] = useState(4);
    const [exponent, setExponent] = useState(4);
    const result = (base ** exponent).toFixed(2);
   
    const handleBaseChange = (e) => {
      e.preventDefault();
      setBase(e.target.value);
    };  
    const handleExponentChange = (e) => {
      e.preventDefault();
      setExponent(e.target.value);
    };  
    return { base, exponent, result, handleBaseChange, handleExponentChange};
  };
   
  export const ExponentCalculator = () => {
    const {base, exponent, result, handleExponentChange, handleBaseChange} = useExponentCalculator();
    // More logic
  };Code language: JavaScript (javascript)

We could move this hook to a separate file for a more prominent separation of concerns.

Additionally, we can further separate our hook into smaller, reusable functions. In this case, we can only extract calculateExponent.

const calculateExponent = (base, exponent) => base ** exponent;
const useExponentCalculator = () => {
  const [base, setBase] = useState(4);
  const [exponent, setExponent] = useState(4);
  const result = calculateExponent(base, exponent).toFixed(2);
  // ...
};Code language: JavaScript (javascript)

Testing these functions is much easier than testing the entire component’s code from the first example. We could test them with any Node.js testing library, which doesn’t even need to support React components.

We now have our framework-specific code (React) in the code for the component and the hook, while our business logic lives in the different functions we defined later (which are framework-agnostic).

Guidelines

Naming

Name custom hooks afterward the component as a concatenation of use and the component’s name (e.g. useExponentCalculator). Call the file the equivalent as the hook. If possible to reuse parts of a custom hook, move it to another file beneath src/hooks.

Separating CSS from JavaScript

Keep the CSS in its own files and don’t mix it with the JavaScript/JSX code. If you’re using a CSS-in-JS library (useStyles), you may want to move this code to another file as well.

Modularity

Split all components into very small components.

Divided Logics

Try not to use state or handlers functions in all components, make dumb/smart components. If a component has only a few lines of JS, it’s not necessary to separate the logic.

Custom Hooks

Handle complicated business logic in separated custom hooks, and we can use custom hooks within other custom hooks.

Last Word

If your codebase doesn’t include a lot of logic and not that big then the benefits of this pattern won’t be too appropriate for you

Feel free to leave any comments for clarification, changes, or improvements. Also, you can contact with iXora Solution expert teams for any consultation or coordination from here.

Add a Comment

Your email address will not be published. Required fields are marked *