In React, components are the building blocks of an application’s UI. Components allow developers to break down the user interface into independent, reusable pieces that can manage their own state and logic.
What is a React Component?
A React component is a JavaScript function or class that optionally accepts inputs (called “props”) and returns a React element that describes how a section of the UI should appear. React then takes care of updating and rendering the component whenever its data changes, leading to a dynamic user experience.
In React, each component encapsulates its structure, logic and styling, making it possible to create a UI by combining multiple components.
Example:
// A simple React component
function Welcome() {
return <h1>Hello, world!</h1>;
}
Types of Components in React
React components come in two main types: Functional Components and Class Components.
a) Functional Components
Functional components are simply JavaScript functions that return a React element. Introduced as the preferred method for defining components, functional components are concise and easy to write. With the addition of React Hooks, functional components now have the capability to manage state and lifecycle methods, which were previously only available to class components.
Example of a Functional Component:
function Greeting(props) {
return <h1>Hello, {props.name}!</h1>;
}
b) Class Components
Class components are ES6 classes that extend React.Component and contain a render() method that returns a React element. While functional components are preferred for new projects, class components are still common in older codebases. Class components can use lifecycle methods, such as componentDidMount and componentWillUnmount.
Example of a Class Component:
import React, { Component } from 'react';
class Greeting extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
Component Structure: Props and State
Components can use props and state to manage data and behavior:
a) Props
Props (short for “properties”) are read-only inputs passed to components from their parent. They enable data to flow from parent to child components, allowing components to remain isolated and reusable.
Example of Using Props:
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// Usage
<Welcome name="Alice" />
In this example, name is a prop passed to the Welcome component, allowing dynamic rendering based on the prop value.
b) State
State is an internal object that allows components to keep track of data that may change over time. Unlike props, state is mutable and can be managed directly by the component. Functional components manage state using the useState Hook, while class components define state as an object.
Example of Using State in a Functional Component:
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>
);
}
Component Composition
React applications are often composed of multiple components arranged in a hierarchical structure. Composition allows developers to nest components within one another, creating complex UIs out of simple, isolated components.
Example of Component Composition:
function App() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
);
}
In this example, App
is a parent component that includes three child components: Header, MainContent, and Footer.
Component Lifecycle (for Class Components)
Each component in React goes through a lifecycle, which includes three main phases: Mounting, Updating, and Unmounting. These phases provide methods that allow developers to execute code at specific points in the component’s lifecycle.
- Mounting: When a component is created and inserted into the DOM.
- constructor( )
- componentDidMount( )
- Updating: When the component is updated (usually due to a change in state or props).
- shouldComponentUpdate()
- componentDidUpdate()
- Unmounting: When the component is removed from the DOM.
- componentWillUnmount()
Example:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
componentDidMount() {
console.log('Component mounted!');
}
componentWillUnmount() {
console.log('Component unmounted!');
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
</div>
);
}
}
Pure Components and Memoization
A pure component is a type of component that renders the same output for the same props and state, preventing unnecessary re-renders. React provides React.PureComponent, a base class that automatically implements shouldComponentUpdate with a shallow prop and state comparison.
In functional components, you can achieve similar optimization using React.memo.
Example of React.memo:
const Greeting = React.memo(function Greeting(props) {
return <h1>Hello, {props.name}</h1>;
});
Higher-Order Components (HOCs)
A Higher-Order Component (HOC) is a function that takes a component as an argument and returns a new component. HOCs are used to reuse component logic and add functionality.
Example of an HOC:
function withGreeting(WrappedComponent) {
return function EnhancedComponent(props) {
return <WrappedComponent {...props} greeting="Hello!" />;
};
}
const EnhancedComponent = withGreeting(SomeComponent);
Rendering Lists of Components
To render a list of components, you can map over an array and return a component for each item in the array. Ensure each item has a unique key
to help React optimize rendering.
Example:
const users = ['Alice', 'Bob', 'Charlie'];
const userList = users.map((user, index) => <li key={index}>{user}</li>);
function UserList() {
return <ul>{userList}</ul>;
}
Reusability and Modularity
React components promote reusability by allowing developers to isolate pieces of UI and logic. By organizing components effectively, applications become easier to maintain and extend over time. Components can be imported and used in multiple places, making them modular and flexible.