function Greeting(props)
{ return <h1>Hello, {props.name}!</h1>; }
React has revolutionized web development by giving developers a powerful tool for creating dynamic, responsive user experiences. The founding principle of React is its component-based architecture, which divides complicated user interfaces into smaller, reusable parts known as components. This simplifies code maintenance and reading, as well as helps in the development of scalable systems. In this blog, we will look at component-based architecture ideas and demonstrate how to apply them to the development of a React application.
What is Component-Based Architecture?
A component-based architecture is an approach rather than a technique for developing UI whereby the UI is built by splitting it into reusable fragments called components. Each component is assigned to handle a particular feature or part of the user interface thus making the application easier to maintain and extend. The main ideas behind this architecture are:
- Reusability: Components can be utilized in numerous places across the application, which helps to avoid writing the same code numerous times. For example, a button component designed for a form can be utilized in a pop-up or another portion of the app.
- Encapsulation: Components handle their own state and behavior, which enhances modularity. Each component has a clear interface and can be built, tested, and maintained independently.
- Separation of Concerns: Each component concentrates on a single task or function, which leads to cleaner and more manageable code. This separation enables developers to understand and work on each component of the application independently.
Benefits of Component-Based Architecture
Improved Code Maintainability
- Smaller, self-contained components are easier to manage and update.
- When a bug is found or a new feature is needed, developers can easily find and fix the specific component without searching through a lot of code.
Enhanced Collaboration
- Teams can work on different components at the same time without causing conflicts.
- This speeds up the overall project timeline and makes better use of team resources.
Scalability
- Adding new features or changing existing ones is simpler with a modular component structure.
- As the application grows, developers can build new components or change existing ones without affecting other parts of the application.
| Aspect | Traditional Monolithic Architecture | Component-Based Architecture |
| Codebase | One large, unified codebase | Broken down into smaller, independent components |
| Scalability | Difficult to manage and scale as the application grows | Easier to scale by adding or modifying individual components |
| Impact of Changes | Changes in the UI can affect the entire application | Changes in one component typically don’t impact others |
| Development Efficiency | Slower, as changes require careful consideration of the entire system | Faster, as developers can work on isolated components |
| Risk of Bugs | Higher risk, as changes in one area can cause bugs in another | Lower risk, as components are independent and self-contained |
Core Concepts of React Components
Understanding Components and Props
Components are the building blocks of a React application. Consider them as JavaScript functions that return HTML. Props (short for properties) are used to transfer data from one component to another, enabling dynamic content rendering.
An example of a basic functioning component with props:
State and Lifecycle
React comes with a built-in object called State that lets components handle their own data. To keep the user interface (UI) up to date with the data, state changes cause re-renders.
Example of a component with state:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
Creating a React Application
Prerequisites
Before building a React application, ensure you have the following:
- Node.js & npm installed on your system. You can check by running:
node -v
npm -v
- Basic understanding of JavaScript and ES6+ features & familiarity with React components (covered in the previous section).
Once you have these in place, you’re ready to go.
Setting up a React Project
Use Create React App to start a new React app:
npx create-react-app todo-app
cd todo-app
npm start
Structuring the Project
Configure your project using a well-defined folder structure:
src/
|-- components/
| |-- TodoItem.jsx
| |-- TodoList.jsx
| |-- TodoForm.jsx
|-- App.jsx
|-- index.js
Building a Todo List Application
Creating and Organizing Components
In this section, we will develop a simple Todo List application, which will include three key components:
- TodoItem: Represents an individual todo item.
- TodoList: Displays a list of todo items.
- TodoForm: Contains a form to add new todo items.
TodoItem Component: The TodoItem component will receive a todo object as a prop and display its content.
function TodoItem({ todo }) {
return <li>{todo.text}</li>;
}
export default TodoIte
TodoList Component: The TodoList component will render a list of TodoItem components. It will receive an array of todos as a prop.
import React from 'react';
import TodoItem from './TodoItem';
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
}
export default TodoList;
TodoForm Component: The TodoForm component will manage the input for adding new todos. It will use local state to keep track of the input value.
import React, { useState } from 'react';
function TodoForm({ addTodo }) {
const [text, setText] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
addTodo({
id: Date.now(),
text,
});
setText('');
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Enter todo"
/>
<button type="submit">Add Todo</button>
</form>
);
}
export default TodoForm;
Managing State and Props Between Components
We now have to manage the application’s whole state within the App component. This component will contain the todos array and the function for adding new todos.
import React, { useState } from 'react';
import TodoList from './components/TodoList';
import TodoForm from './components/TodoForm';
function App() {
const [todos, setTodos] = useState([]);
const addTodo = (todo) => {
setTodos([...todos, todo]);
};
return (
<div>
<h1>Todo List</h1>
<TodoForm addTodo={addTodo} />
<TodoList todos={todos} />
</div>
);
}
export default App;
Best Practices for Component-Based Architecture in React
- To maintain your application easy to handle and grow, use these recommended practices:
- Single Responsibility Principle: Each component should be capable of performing a single task well. This facilitates understanding, testing, and maintenance. For example, the TodoItem component displays a single todo, whereas the TodoForm component handles input and submission.
- Reusability and Modularity: Create components that can be used in multiple places throughout your app. This reduces duplicate code and makes maintenance easier. For example, you could design a Button component that can be utilized in different forms in all around the application.
- Proper State Management
- Lifting State Up: Place the state to the nearest common parent of the components that require it. This allows components to communicate more effectively and avoids passing props down multiple tiers.
- Context API: To handle the global state without having to use prop drilling, use the Context API. This is especially useful in larger projects with multiple nested components.
- Using Hooks for Functional Components: React hooks such as useState and useEffect enable functional components to manage state and lifecycle methods. As a result, managing complicated logic without the need for class components is made easier for functional components.
Refactoring the Todo List Application for Better Practices
To improve our Todo List application, we may develop a separate Button component for reuse:
import React from 'react';
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
export default Button;
TodoForm‘s button can now be replaced with our Button component:
import Button from './Button';
// Inside TodoForm
<Button onClick={handleSubmit}>Add Todo</Button>
Advanced Component Patterns
Higher-Order Components (HOCs)
Functions known as HOCs accept a component and return a new one. This allows for the reuse of component logic, such as authentication checks or data fetching.
function withAuth(WrappedComponent) {
return function AuthHOC(props) {
const isAuthenticated = false; // check authentication logic
if (!isAuthenticated) {
return <Redirect to="/login" />;
}
return <WrappedComponent {...props} />;
};
}
Render Props
Render Props is a mechanism for exchanging code between React components, utilizing a prop whose value is a function. This provides additional flexibility in component rendering.
function DataProvider({ render }) {
const data = fetchData(); // Assume this function fetches data
return <div>{render(data)}</div>;
}
// Usage
<DataProvider render={(data) => <SomeComponent data={data} />} />
Compound Components
Compound components enable you to design a collection of components that interact to form a complicated UI element. This pattern allows you to encapsulate shared functionality while providing users with a simple API.
Example of Compound Components:
function Tabs({ children }) {
const [activeIndex, setActiveIndex] = useState(0);
const handleTabClick = (index) => {
setActiveIndex(index);
};
return (
<div>
<ul>
{React.Children.map(children, (child, index) =>
React.cloneElement(child, {
isActive: index === activeIndex,
onClick: () => handleTabClick(index),
})
)}
</ul>
<div>{children[activeIndex].props.children}</div>
</div>
);
}
function Tab({ isActive, onClick, children }) {
return (
<li onClick={onClick} style=>
{children}
</li>
);
}
// Usage
<Tabs>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</Tabs>
Testing React Components
Why Testing Matters in Component-Based Architecture
Testing helps to confirm that your components are functioning properly. It also makes it easier to change the code with assurance. A well-tested app has fewer bugs and problems, which speeds up development.
Tools and Libraries for Testing
- Jest: A popular testing tool for React that offers many features for testing JavaScript apps.
- React Testing Library: A library designed for testing React components, focusing on how users interact with them.
Writing Unit Tests for Components
When you write unit tests, focus on how individual components behave instead of how they are built. This makes your tests stronger against changes in the component design.
Testing the Todo List Application Components
Here’s an example of a unit test for the TodoForm component to ensure it correctly adds a new todo:
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import TodoForm from './TodoForm';
test('adds a new todo', () => {
const addTodo = jest.fn();
const { getByText, getByPlaceholderText } = render(<TodoForm addTodo={addTodo} />);
fireEvent.change(getByPlaceholderText('Enter todo'), { target: { value: 'Learn React' } });
fireEvent.click(getByText('Add Todo'));
expect(addTodo).toHaveBeenCalledWith(expect.objectContaining({ text: 'Learn React' }));
});
Running Tests
To run the tests, you can use the following command:
npm test
This command will execute all the tests in your application and provide feedback on their results.
Conclusion
Component-based architecture in React is a great way to build apps that are easy to scale and maintain. By following the ideas and best practices in this blog, you can make strong React apps that are simple to manage and grow.
In this post, we looked at key ideas about React components, like props, state, and lifecycle methods. We built a basic Todo List app to show how to set up components and handle state well. We also talked about advanced methods like Higher-Order Components, Render Props, and Compound Components to improve your component design.
Finally, we discussed the necessity of testing in component-based programming and demonstrated how to construct unit tests for your components. By implementing these techniques in your React projects, you’ll be prepared to tackle complex apps with confidence.
18/03/2025

.png?width=170&height=122&name=glassdoor%20(1).png)
