React — Components
React components are the building blocks of a React application. They are reusable, self-contained pieces of code that define a part of the user interface (UI). Components can be simple elements like buttons or more complex structures like entire pages.
Types of React Components
- Functional Components:
- These are JavaScript functions that return React elements. They are simpler and easier to write than class components.
- With the introduction of React Hooks, functional components can now manage state and handle side effects, making them as powerful as class components.
function Greeting(props) {
return <h1>Hello, {props.name}</h1>;
}
2. Class Components:
- These are ES6 classes that extend from
React.Component
and must contain arender()
method that returns React elements. - Class components can have state and lifecycle methods.
class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Composition
React components can be composed together to create complex UIs. This involves nesting components within each other and passing data between them via props.
function App() {
return (
<div>
<Greeting name="Alice" />
<Greeting name="Bob" />
<Greeting name="Charlie" />
</div>
);
}
Comparison
When deciding between using functional components and class components in React, it’s important to consider the specific use cases and the features each type offers. Here are some common scenarios where one might be preferred over the other:
Simplicity and Readability
Functional components are generally simpler and more readable, making them a great choice for components that:
- Do not require state or lifecycle methods.
- Are primarily focused on rendering UI based on props.
function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
}
Hooks
With the introduction of Hooks, functional components can now manage state and side effects, making them as powerful as class components. Use functional components when you need:
- State management using
useState
. - Side effects and lifecycle methods using
useEffect
. - Context with
useContext
. - Other React Hooks like
useReducer
,useMemo
, etc.
import React, { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
Performance
Functional components can be more performant, especially when using Hooks like React.memo
for memoization, which prevents unnecessary re-renders.
import React, { memo } from 'react';
const Button = memo(function Button({ onClick, children }) {
console.log('Button render');
return <button onClick={onClick}>{children}</button>;
});
Legacy Code
In existing projects that already use class components extensively, it might be consistent and less disruptive to continue using them, especially when:
- The team is more familiar with class components.
- There is substantial existing code written with class components.
Complex Lifecycle Methods
Class components can be easier to manage when dealing with complex lifecycle scenarios where multiple lifecycle methods need to be implemented (e.g., componentDidMount
, componentDidUpdate
, componentWillUnmount
).
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Error Boundaries
As of now, only class components can be used to create error boundaries. An error boundary is a React component that catches JavaScript errors anywhere in its child component tree, logs those errors, and displays a fallback UI instead of the component tree that crashed.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
// You can also log the error to an error reporting service
console.log(error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
Conclusion
React components are essential for creating modular, reusable, and maintainable code in React applications. Understanding how to create and manage components, use props and state, and leverage hooks and lifecycle methods is key to effectively working with React.