Xlera8

How to separate logic from UI with React components | by Esmat Ibrahim | Codementor

Separating the logic and UI makes it easier to write and test components.
The main goal of this article is to make sure we are writing our new components in a way where each component cares only about its data, error handling, loading, and any other needed logic (we will discuss sharing data between components in a different article).

Why is this important?

  • Scalability: It is indeed high scalable.
  • Removability: Deleting a component should not be an issue after today, no breaking changes or regressions happen.
  • Moveability: The ability to use the component anywhere, and maybe across projects which use the same API.

Hands-On

Let’s dive into the code and show 2 examples before and after the refactoring.

Let’s take the following example and try to understand how to make it happen:

import React, { useMemo } from 'react';
import useAxios from 'axios-hooks'; const TodoList = () => { const [{ data, loading, error }] = useAxios( `/`); const todoItems = useMemo( () => (data ? data.filter((item) => item.status === 'TODO') : []), [data], ); if (loading) return <div>Loading...</div>; if (error) return <div>Error</div>; return <div>{JSON.stringify(todoItems)}</div>;
}; export default TodoList;

As you can see above, we have a simple example of implementing the To-Do list by requesting the data through remote API configuration using axios.

Can you notice the simple calculation to extract the items with status = TODO? If your answer is yes, then we are on the same page.

As we explained at the beginning of the article, our mission is to isolate the logic from the UI, but in the above example we have logic in the same JS file, let’s see what should be done to achieve our mission.

So, first of all, we need to take the logic out of the component.

import { useMemo } from 'react';
import useAxios from 'axios-hooks'; const useTodoList = () => { const [{ data, loading, error }] = useAxios( `/`); const todoItems = useMemo( () => (data ? data.filter((item) => item.status === 'TODO') : []), [data], ); return [{ data, loading, error, todoItems }];
}; export default useTodoList;

As you can see, we called it “useTodoList”. This hook should contain the logic of the TodoList component.

You can notice that we don’t have UI in the file, this is a custom hook.

But how should the main component “TodoList” look like?

import React from 'react'; import useTodoList from './useTodoList'; const TodoList = () => { const [{ data, loading, error, todoItems }] = useTodoList(); if (loading) return <div>Loading...</div>; if (error) return <div>Error</div>; return <div>{JSON.stringify(todoItems)}</div>;
}; export default TodoList;

Simple, ha? This is exactly why it is important to build components where the business logic is not part of the UI and deal with the custom hook as an API for your component.

What about the communication between components when each component is working in standalone mode? This will be the interesting topic we’ll cover in the next article.

Chat with us

Hi there! How can I help you?