What is class component and Functional component?

Defining Class Components and Functional Components

Class Components

A class component in React is defined as an ES6 class that extends the React.Component base class. It must include a render() method that returns the React elements to display. Here is the basic syntax:

class MyComponent extends React.Component {
  render() {
    return <h1>Hello from a class component!</h1>;
  }
}

Class components can also define a constructor to initialize state and bind event handler methods. The constructor takes props as an argument and must call super(props) before using this:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState({count: this.state.count + 1});
  }

  render() {
    return (
      <div>
        <h1>Count: {this.state.count}</h1>
        <button onClick={this.handleClick}>Increment</button>
      </div>
    );
  }
}

Functional Components

A functional component is a plain JavaScript function that accepts props as an argument and returns a React element. Here’s a simple example:

function MyComponent(props) {
  return <h1>Hello, {props.name}!</h1>;
}

Functional components are also known as stateless components because they don’t manage their own state. However, you can use hooks like useState to add state management to functional components (more on this later).

One key benefit of functional components is that they are very easy to read and test since they are just functions without much boilerplate. This encourages writing small, focused components.

Syntax Differences Between Class and Functional Components

Let’s look at the syntax differences between class and functional components in more detail:

Feature Class Component Functional Component
Declaration Declared using an ES6 class that extends React.Component Declared as a JavaScript function
Handling props Accessed via this.props Received as an argument to the function
State management Use this.state and this.setState() Use React hooks like useState
Lifecycle methods Provide componentDidMount, componentDidUpdate, etc Use useEffect hook to emulate lifecycle behavior
Event handlers Defined as methods on the class, need to bind this Defined as plain functions, no this binding

Here’s a comparison showing the same component implemented as a class vs a function:

/* Class component */
class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}
/* Functional component */  
function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

As you can see, the functional component is more concise. It’s a plain function rather than a class, does not have a constructor, and accesses props as a function argument rather than this.props.

Managing State and Lifecycle in Class Components

One key feature of class components is the ability to manage internal state and respond to lifecycle events. Let’s look at how these work.

State Management

Class components can define and update their own state using this.state and this.setState(). The state is a plain JavaScript object that holds data that may change over time. When the state is updated via setState(), React automatically re-renders the component to reflect the new state. Here’s an example:

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  increment() {
    this.setState({count: this.state.count + 1});
  }

  render() {
    return (
      <div>
        <h1>Count: {this.state.count}</h1>
        <button onClick={() => this.increment()}>Increment</button>
      </div>
    );
  }
}

In this example, the Counter component initializes its state with a count of 0. It defines an increment method that updates the state by calling this.setState(). The new state is merged with the existing state. Finally, the render() method displays the current count and a button that triggers the increment method when clicked.

Lifecycle Methods

Class components also provide several lifecycle methods that allow you to hook into different stages of a component’s life and perform actions. Here are some of the most commonly used lifecycle methods:

Method Description
constructor(props) Initialize state, bind methods
render() Return React elements to render
componentDidMount() Runs after component output rendered to DOM
componentDidUpdate(prevProps, prevState) Runs after an update, can compare previous props/state
componentWillUnmount() Teardown/cleanup before component removed from DOM

Here’s an example showing how to use lifecycle methods:

class PostList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      posts: []  
    };
  }

  componentDidMount() {
    // Fetch posts from API after mount
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(response => response.json())
      .then(data => this.setState({posts: data}));
  }

  componentDidUpdate(prevProps) {
    // Refetch posts if props.category changes  
    if (prevProps.category !== this.props.category) {
      fetch(`https://jsonplaceholder.typicode.com/posts?category=${this.props.category}`)
        .then(response => response.json())  
        .then(data => this.setState({posts: data}));
    }
  }

  componentWillUnmount() {
    // Cancel any pending requests  
  }

  render() {
    return (
      <div>
        <h1>Posts in {this.props.category}</h1>
        <ul>
          {this.state.posts.map(post => (
            <li key={post.id}>{post.title}</li>
          ))}
        </ul>
      </div>
    );
  }
}

In this example:

  1. The constructor initializes the posts state as an empty array
  2. componentDidMount fetches posts after the first render
  3. componentDidUpdate refetches posts if the category prop changes
  4. componentWillUnmount cancels any pending requests before unmount
  5. render maps over the posts state to display a list of post titles

Lifecycle methods are useful for performing side effects like fetching data, subscribing to event listeners, and doing cleanup.

Handling Props in Functional Components

Functional components are simpler than class components because they don’t have state or lifecycle methods. However, they can still receive and use props to configure their behavior and rendering.

Props are a way to pass data from a parent component down to its child components. They are read-only and should not be modified by the child component.

Here’s an example of a functional component that receives props:

function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

You can then pass props to this component when rendering it:

<Greeting name="John" />

This will render “Hello, John!” on the page.

You can also use object destructuring to extract specific props, which can make the code more readable:

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;  
}

Default Props

Functional components can specify default values for props using the defaultProps property. These will be used if the prop is not passed by the parent:

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

Greeting.defaultProps = {
  name: 'stranger'  
};

Now if you render without specifying a name prop, it will default to “Hello, stranger!”.

Prop Types

You can also define the expected types of your component’s props using prop-types. This helps catch bugs by warning you if the wrong type of data is passed. Here’s how to add prop types:

import PropTypes from 'prop-types';

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;  
}

Greeting.propTypes = {
  name: PropTypes.string
};

Now if you pass a non-string value for the name prop, you’ll see a warning in the JavaScript console.

Prop types can be required or optional, and you can specify more complex types like arrays and objects. Validating props is a good way to make your components more robust.

Performance Considerations

When deciding between class and functional components, performance is another factor to consider. In general, functional components have less overhead than class components because they don’t have a separate instance with state and lifecycle methods.

However, the performance difference is usually negligible unless you’re rendering a very large number of components. In most cases, it’s more important to focus on writing clean, modular code than to micro-optimize your component types.

That said, there are a few situations where functional components can provide a performance boost:

Pure Components

If your component only depends on its props and doesn’t have any state or side effects, you can declare it as a pure function. This allows React to optimize rendering by skipping re-renders if the props haven’t changed.

To create a pure component, simply return the same output for the same input props:

function Button({ text }) {
  return <button>{text}</button>;
}

As long as the text prop doesn’t change, React can safely reuse the previous rendered output.

Memoization

Memoization is a technique for caching the results of expensive calculations so you don’t have to recompute them on every render. You can use the useMemo and useCallback hooks to memoize values and functions in functional components.

For example, say you have a component that renders a filtered list:

function FilteredList({ list, query }) {
  const filteredList = list.filter(item => item.name.includes(query));

  return (
    <ul>
      {filteredList.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

The filteredList will be recomputed every time the component re-renders, even if list and query haven’t changed. You can optimize this by memoizing the filtered list:

import { useMemo } from 'react';

function FilteredList({ list, query }) {
  const filteredList = useMemo(() => 
    list.filter(item => item.name.includes(query)), 
    [list, query]
  );

  return (
    <ul>
      {filteredList.map(item => (
        <li key={item.id}>{item.name}</li>  
      ))}
    </ul>
  );
}  

Now filteredList will only be recomputed if list or query changes. This can help avoid unnecessary work and improve performance.

When to Use Class vs Functional Components

So when should you use class components vs functional components? Here are some general guidelines:

Use Class Components When

  • You need to manage state
  • You need to use lifecycle methods
  • You need to add event listeners or perform other side effects
  • You’re working with an older codebase that predates hooks

Use Functional Components When

  • Your component is stateless (only receives props)
  • Your component doesn’t need lifecycle methods
  • Your component can be expressed as a pure function of its props
  • You’re working on a new project or refactoring an existing one

Keep in mind that with the introduction of hooks, you can now manage state and lifecycle effects in functional components too. So the decision often comes down to personal preference, coding style, and consistency with the existing codebase.

In general, lean towards using functional components by default and only use class components when you really need them. Functional components are easier to read, test, and reason about, and they encourage good practices like composition and separation of concerns.

Frequently Asked Questions

What are the differences between class and functional components?

The main differences are:

  • Class components are declared using an ES6 class, functional components are plain JavaScript functions
  • Class components have their own state and lifecycle methods, functional components rely on hooks
  • Class components use this to access props and methods, functional components receive props as function arguments

Refer to the syntax differences table earlier in this article for a more detailed comparison.

Can I use state and lifecycle methods in functional components?

Yes, as of React 16.8 you can use hooks to add state and lifecycle effects to functional components. The useState hook lets you declare and update state, while the useEffect hook lets you perform side effects like fetching data or subscribing to events.

For example, here’s how to manage state in a functional component with useState:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  function increment() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

And here’s how to fetch data with useEffect:

import { useState, useEffect } from 'react';

function PostList() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(response => response.json())
      .then(data => setPosts(data));
  }, []);

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Hooks let you use state and lifecycle features in functional components without the overhead of class components.

When should I convert a class component to a functional component?

Consider converting a class component to a functional component when:

  • The component only receives props and doesn’t have its own state
  • The component doesn’t use lifecycle methods
  • You want to simplify the component and make it easier to test
  • You’re already using hooks in other parts of your codebase

The process of converting involves:

  1. Changing the class declaration to a function
  2. Removing the render method and putting the return statement directly in the function body
  3. Replacing this.props with the props argument
  4. Replacing this.setState and lifecycle methods with hooks like useState and useEffect

For example, here’s a class component:

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }  
}

And here’s the equivalent functional component:

function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

Are there any cases where I shouldn’t use functional components?

Functional components are suitable for most use cases, but there are a few situations where you might want to stick with class components:

  • You need to use an older lifecycle method like componentWillReceiveProps that doesn’t have a hook equivalent
  • You’re working with a large, complex component that would be difficult to refactor
  • You’re using a third-party library that requires class components
  • You simply prefer the syntax and organization of class components

Ultimately, the choice between class and functional components is a judgement call based on your specific needs and preferences. Many codebases use a mix of both class and functional components, and that’s perfectly fine.

Can I use class and functional components together?

Yes, you can mix

CATEGORIES:

Uncategorized

Tags:

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *

Latest Comments

No comments to show.