Introduction
React is an open-source library popularly used for building interfaces. React provides developers with a structured and efficient way to create interactive and very dynamic UI components. At its core React operates on the principle of component-based architecture. This means you can break down the User Interface into bite-sized reusable pieces of code called components. This makes it easier to manage and maintain complex applications.
What are these components?
Components are the building blocks of a user interface. They are self-contained and reusable pieces of code that can contain a specific part of functionality and presentation. When building an application, various components have to be put together to make the whole application work properly or as intended. Although these components work in conjunction with each other, they are all independent.
Types of React Components
There are mainly two types of react components
- Class Components
- Functional Components
Class Components
Class components are Javascript classes that make use of the ES6 syntax to extend the 'React.component' class. This was the initial way of defining components in React before the functional components and hooks were introduced. Class components can be referred to as smart or stateful as they were used to implement logic and state before the introduction of hooks. Using the "this" keyword, props could be passed down from parent to child components.
The Anatomy of the Class Component
The key features of a class component are discussed below.
- The Class declaration
The class component usually starts with a declaration that extends the React.component as shown below.
import react, {Component} from 'react'
class Article extends Component {
}
- The Constructor This is used to initialize the state of the component and bind event handlers to the component. The code below shows the constructor. This receives props as an argument to represent the initial properties passed from the parent element to this component. The next line shows super(props)' which calls the constructor of the parent class React.Component and passes the props to it. This is required to properly initialize the component and make sure that React sets up the props correctly. The component's state is initialized with this.state = { count: 0 } where state is an object that holds data that can change over time and trigger updates to the component's UI when it does. In this case, it initializes the count property to 0, which will be used to keep track of a count in the component's state. The last line: this.handleClick = this.handleClick.bind(this) binds the this context to the handleClick method.
constructor(props) {
super(props);
this.state = {
count: 0,
};
this.handleClick = this.handleClick.bind(this);
}
- The render method This returns the JSX representation of the component's UI.
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>Increment</button>
</div>
);
}
- State Management Class components manage their states using this.state. This is used to store and update data over time thus making them dynamic. The code below updates the count property with an increment of 1.
handleClick() {
this.setState({ count: this.state.count + 1 });
}
- Lifecycle Methods Class components also have lifecycle methods that allow developers to hook into the different stages of a component's lifecycle. These methods enable you to perform actions like initialization, data fetching, DOM manipulation, and cleanup.
Functional Components
With the development of React hooks, functional components became more popular as its limitation was the ability to manage states. Functional components are javascript functions that return JSX to define and render a user interface. Because of their simplicity and readability, they are now preferred over class components although for larger-scale applications where you need more control over the component's lifecycle, the class component is preferred.
The Basic Structure of Functional Components.
- Function Declaration. At its core, the functional component is a Javascript function and with the development of the ES6 syntax it can also be defined as an arrow function
// Regular function
function Article() {
return (
// JSX goes here
)
}
//Arrow function
const Article = () => {
return (
// JSX goes here
)
}
- Component Logic Within the body of the function, you can write Javascript code to define the component's behavior with respect to other components or within itself. The JSX return statement is the most crucial part of the component as the User Interface is rendered here.
function Article () {
const username = "John";
const greetingMessage = `Hello, ${username}!`;
return <div>{greetingMessage}</div>;
}
- The Export Statement The export statement makes the component accessible to other parts of the application. The export keyword is used and when it is the only component being exported from the file, the export default keyword can be used.
export function Article() {
// Component logic and JSX rendering
}
// OR
export default function Article() {
// Component logic and JSX rendering
}
Functional Components Vs Class Components
They both have their highlights/benefits and drawbacks. They are discussed below.
Highlights
Functional Components
- They are simpler and more concise than class components, making them easier to read and understand and they are an excellent choice, especially for beginners.
- They do not use the this keyword thereby making your code more predictable and reducing the chances of bugs in your code.
- Although they are referred to as 'dumb' and 'stateless', they work seamlessly with React hooks allowing them to manage states effectively.
- They encourage the creation of reusable UI elements. They are more modular, making it easier to compose and share code between different parts of your application.
- With React's React.memo and other optimization techniques, functional components can be optimized to minimize unnecessary re-renders, improving application performance.
- Functional components promote immutability and pure functions and this leads to cleaner and more maintainable code. This aligns well with the functional programming paradigm
- Because the React community has shifted its focus toward functional components and hooks, there are more resources, tutorials, and community support available for developers using functional components.
Class Components
- Class components have access to React's lifecycle methods, such as componentDidMount, componentDidUpdate, and componentWillUnmount. These methods allow for fine-grained control over component behavior during its lifecycle.
- They can store instance-specific data in their state, making them suitable for scenarios where you need to manage complex states with multiple properties.
- They are also still widely used in existing React codebases, and they continue to be supported by the React library. They can be essential for maintaining and updating legacy projects which are essentially older projects with outdated technology stacks or limited documentation.
- Class components explicitly define the component's structure and behavior, which can be advantageous when working on projects where clarity and explicitness are essential.
- Class components are well-suited for certain advanced patterns, such as implementing higher-order components (HOCs) and creating custom render methods.
Drawbacks
Functional Components
- Functional components couldn't use lifecycle methods until the introduction of React 18 (Concurrent Mode). This could make it challenging to manage side effects and component updates in complex scenarios.
- They don't have a this keyword. While this can be seen as a benefit, it can also be a limitation when you need to access instance-specific data or methods.
- Sharing logic between functional components can be less intuitive compared to class components using higher-order components (HOCs) or render props. Although custom hooks have improved this aspect, it can still be more complex than class component reuse.
- Passing props down multiple levels of nested components (prop drilling) can become cumbersome and lead to less readable code. This is particularly frustrating based on my experience.
Class Component
- Class components can be more complex than functional components. They require a constructor for state initialization and lifecycle methods like componentDidMount, componentDidUpdate, etc., which can make the code harder to follow. This is a benefit but it is also a drawback.
- They may have performance implications due to their reliance on lifecycle methods. For example, unnecessary re-renders can occur if not managed carefully.
- Class components tend to be more verbose than functional components. This verbosity can make the code harder to read and maintain, especially in larger projects.
- For developers new to React, understanding class components, their lifecycle methods, and the this keyword can pose a steeper learning curve compared to functional components.
- With the introduction of hooks, the React community has shifted its focus towards functional components. This means that class components may receive less attention in terms of updates, improvements, and community resources.
Conclusion
As a developer, the choice of component type to use is not determined by which type has more pros or cons. This balls down to the particular use case, project requirements, and your preferences. While functional components have become increasingly popular in many modern React applications, Class components remain relevant, especially in cases where their unique capabilities are needed or when dealing with existing codebases.
Latest comments (2)
Great post, you know, I'm a SvelteKit fan, but I have been considering to learn React due to its large demand, and I could understand your examples, or at least most of it, so it felt kind of like a good intro to React. 😆
React team recommends defining components as functions instead of classes.