Type to generate UI components from text

OR

Browse thousands of MUI, Tailwind, React components that are fully customizable and responsive.

Explore Components

React Functional Components: How to Effectively Utilize Them

React has become influential in modern web development because it can build dynamic and interactive user interfaces. In the core of React lies components, a modular building block that forms the various parts of a web application. Every React application contains a base component encapsulating the rest of the React elements or functionalities.

Recently, functional components have gained widespread adoption and are now considered the recommended method for creating reusable code units in React. This shift was facilitated by the advent of React Hooks, enabling developers to enhance how each application component manages the user interface (UI) logic.

This article will explore more sophisticated approaches to managing application logic using React functional components.

Understanding the Basics of React Components

React components allow developers to break down the UI into smaller manageable bits that encapsulate a specific part or feature of the UI within its own defined scope. In essence, React components are self-contained, reusable units of code that represent distinct elements or features in a user interface.

There are two types of React components, including:

  • Class components

  • Function components

Furthermore, it is a standard convention to capitalize the names of React components, whether they are functional or class components. This approach helps differentiate them from HTML elements.

On that note, accelerate your project’s development with Purecode AI’s ready-to-use UI components. Explore now for quick generation and seamless integration of responsive custom components into your project.

React Class components

React class components are components built based on the Javascript ES6 classes syntax. They are an extension of the React.Component API and contains a render method that returns a React element. Consider the following code example:

class Navbar extends React.Component {
  render() {
    return (
     <nav>
       <div>
         <ul>
          <li><a>Home</a></li>
          <li><a>Services</a></li>
         </ul>
       </div>
     </nav>;
  }
}

React Functional Components

Class components have been a fundamental part of React since its early days. However, introducing Hooks has elevated functional components to a central role in modern React development. This upgrade is evident in how functional components can achieve most class-based component functionality with less code using Hooks.

Therefore, functional components are Javascript functions that are enhanced to take props as a function argument and return the data in valid JSX form.

Now that we have briefly discussed the two types of React components. Let us explore how the two types of React components differ.

Differences Between Functional Component and Class Component

This section covers the differences between the two types of React components to help us understand what makes functional components more efficient than class components. This is illustrated by the table below:

Class componentFunctional component
It has a local state by default,It is stateless by default.
It is an ES6 JavaScript class that uses a render method to display React elements.It is a JavaScript pure function that returns a React element.
It is capable of managing its state and lifecycle events.It requires hooks to handle state and lifecycle events.
It is less reusable and efficient.Its ability to use hooks makes it reusable, efficient, and maintainable.

Core concepts of React functional components: An Overview

Functional components, introduced to React as a simpler alternative to class components, were initially designed to handle presentation logic without managing stateful logic. The sole purpose of a function component was to take props and return valid JavaScript XML (JSX) that the browser can display. However, Hook’s introduction in React version 16.8 gave functional components the capability to manage state, expanding their role in handling both UI and stateful logic and lifecycle events. React Hooks ensures the possibility of conveniently building complex applications with just functional components. Hence, function components have become the preferred way of modern React application development due to their simplicity and clarity. Consider the following code example:

import React from 'react';

function App() {
  const greeting = 'Hello Function Component!';

  return <h1>{greeting}</h1>;
}

export default App;

Stateless and Stateful Functional Component

Stateless function components are components that receive props and output a JSX the browser can display. A stateless function component doesn’t manage its state or have any side effects. To learn more about stateless function components, check out the tutorial video below:

Moreover, Hooks enable function components to handle state and side effects, resulting in function components with state. A later section will discuss the React Hooks available to functional components. The code example below shows how to create a stateful function component:

import React, { useState } from 'react';

function App() {
  return <Headline />;
};

function Headline () {
  const [greeting, setGreeting] = useState(
    'Hello Function Component!'
  );

  return <h1>{greeting}</h1>;
};

export default App;

Crafting Functional Components with Arrow Functions

The introduction of JavaScript ES6 provided a new programming concept that allows developers to express a function as Lambda (Arrow function). Therefore, a function component created with an arrow function is called a React arrow function component. Let’s refactor the code above to use an arrow function:

import React, { useState } from 'react';

const App = () => (
  <Headline />;
);

const Headline = () => {
  const [greeting, setGreeting] = useState(
    'Hello Function Component!'
  );

  return <h1>{greeting}</h1>;
};

export default App;

Pure Function Component

Pure function components in React adhere to the pure function concept in functional programming, ensuring that they consistently produce the same value for the same input values. Hence, a function component will always have pure rendering logic when it returns the same output if its props, state, and context haven’t changed. This method ensures the function component does not re-render whenever the parent component re-renders as long as its parameters and state remain the same. The memo API can create a pure component. This is illustrated below:

import { memo, useState } from 'react';

const Greeting = memo(function Greeting({ name }) {
  console.log("Greeting was rendered at", new Date().toLocaleTimeString());
  return <h3>Hello{name && ', '}{name}!</h3>;
});

export default function MyApp() {
  const [name, setName] = useState('');
  const [address, setAddress] = useState('');
  return (
    <>
      <label>
        Name{': '}
        <input value={name} onChange={e => setName(e.target.value)} />
      </label>
      <label>
        Address{': '}
        <input value={address} onChange={e => setAddress(e.target.value)} />
      </label>
      <Greeting name={name} />
    </>
  );
}

Utilizing Props in Function Components

The props object in a React functional component essentially serves as a function parameter that allows developers to pass data from parent to child components enabling a function component to render dynamic data. It is a read-only, immutable object. Typically, JavaScript object destructuring is employed to showcase the component props’ composition, providing developers with insights into the necessary props for a React component UI logic. The following code example will aid in clarifying this concept:

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

export default function App() {
  return (
    <>
      <Greeting name="John" />
      <Greeting name="Ronaldo" />
      <Greeting name="Jeniffer" />
    </>
  );
}
A graphical representation of various name prop values being passed to the Greeting component.

Controlled vs Uncontrolled Function Components

In React, the terms controlled and uncontrolled component are often used in the context of form elements, describing different approaches to managing and updating the value of the form elements.

In that essence, a controlled component’s state is managed by the parent component through props, enabling the parent component to specify the behavior of the React element. An uncontrolled component is one whose state is managed locally without the influence of external components.

Since uncontrolled function components require less configuration, they’re easier to use within their parents but are less flexible to coordinate. On the other hand, controlled functional components need the parent components to configure them fully with props.

The following code example shows how to create a controlled component:

import { useState } from "react";

function TextInput({
  type = "",
  value = "",
  name = "",
  placeholder = "",
  onchange,
}) {
  return (
    <div style={{ padding: "0.5rem 0" }}>
      <input
        type={type}
        value={value}
        name={name}
        placeholder={placeholder}
        onChange={onchange}
      />
    </div>
  );
}

export default function App() {
  const [inputs, setInputs] = useState({ name: "", email: "" });

  const changeHandler = (e) => {
    const name = e.target.name;
    const value = e.target.value;
    setInputs((prevInputs) => ({ ...prevInputs, [name]: value }));
  };

  return (
    <>
      <TextInput
        type="text"
        value={inputs.name}
        name="name"
        placeholder="Enter name..."
        onchange={changeHandler}
      />
      <TextInput
        type="email"
        value={inputs.email}
        name="email"
        placeholder="Enter email..."
        onchange={changeHandler}
      />
    </>
  );
}

Now let’s refactor the code above to create an uncontrolled component:

import { useRef, forwardRef } from "react";

const TextInput = forwardRef(function TextInput(
  { type = "", name = "", placeholder = "" },
  ref
) {
  return (
    <div style={{ padding: "0.5rem 0" }}>
      <input type={type} name={name} ref={ref} placeholder={placeholder} />
    </div>
  );
});

export default function App() {
  const nameRef = useRef(null);
  const emailRef = useRef(null);

  const doSomething = () => {
    console.log({ name: nameRef.current.value, email: emailRef.current.value });
  };

  return (
    <>
      <TextInput
        type="text"
        name="name"
        ref={nameRef}
        placeholder="Enter name..."
      />
      <TextInput
        type="email"
        ref={emailRef}
        name="email"
        placeholder="Enter email..."
      />
      <button type="button" onClick={doSomething}>
        Submit
      </button>
    </>
  );
}

https://res.cloudinary.com/da8vqkdmt/image/upload/v1705565627/uncontrolled_yih1ny.gif

When building functional components, it is vital to determine whether to acquire the information through props (controlled) or state (uncontrolled).

Advanced Techniques in React Functional Components

So far, we have only been able to discuss the core concepts of React functional components. This section covers the advanced techniques that allow developers to leverage features necessary to enhance a component’s functionality, maintainability, and performance.

Exploring Hooks in React Function Components

Previously, we briefly discussed Hooks and their capability to enhance function components. However, what exactly are React Hooks?

React Hooks are functions that allow function components to manage state, handle lifecycle events, and use other React features that were previously exclusively available to class components. They aim to simplify component logic, reuse code, and ensure easy understanding and maintenance of functional components. The commonly used built-in Hooks include the following:

  • useState

  • useEffect

  • useContext

  • useMemo

  • useCallback

  • useReducer

  • useRef

  • useLayoutEffect, etc.

Rules Guiding the Use of Hooks

There are set of rules guiding the effective use of Hooks in React, these include the following:

  • Only call Hooks at the top level of the body of a React functional component or custom Hook.

    import {useState} from 'react';
    function Counter() {
      // ✅ Good: top-level in a function component
      const [count, setCount] = useState(0);
      // ...
    }
    
    function useWindowWidth() {
      // ✅ Good: top-level in a custom Hook
      const [width, setWidth] = useState(window.innerWidth);
      // ...
    }
  • Avoid calling Hooks inside conditions or loops.

  • Refrain from Hooks after a conditional return statement.

  • Do not call Hooks inside functions passed to event handlers or Hooks like useMemo, useCallback, useReducer, etc.

  • Ensure not to call Hooks in class components.

Lifecycle of React Functional Component

React component’s lifecycle refers to the stages the component undergoes, from its creation to re-rendering and its eventual removal from the DOM.

Before the introduction of Hooks in React, managing a component’s lifecycle was exclusively available to class components. Now, functional components can handle lifecycle processes more efficiently with the help of Hooks. The lifecycle processes include the following three stages:

  1. Mounting stage

  2. Updating/re-rendering stage

  3. Unmounting stage

Mounting stage

functional component is typically created and rendered on the DOM in this stage. This stage generally involves initializing the component’s state and other variables needed when a component first renders.

Updating/re-rendering stage

This stage occurs when the component re-renders due to changes in props or state. This phase results in a re-initialization of state or variables with updated values. In addition, employing a useEffect Hook without a dependency array may lead to unintended component updates, potentially causing an update loop, specifically when utilized in conjunction with asynchronous requests or JavaScript native timer methods. Consider the example below:

import { useEffect, useState } from "react";

export default function App() {
  const [counter, setCounter] = useState(0);
  useEffect(() => {
    setTimeout(() => {
      setCounter((prev) => prev + 1);
    }, 1000);
  });
  return (
    <>
      <p>{counter}</p>
    </>
  );
}

Furthermore, failing to clean up after timers inside a useEffect Hook can lead to memory leaks. This event happens because the timer references the component, preventing it from being garbage-collected.

Unmounting stage

This stage involves removing the component from the DOM. The cleanup function of the useEffect Hook acts similarly to the componentWillUnmount in class components. Applying the cleanup function to unsubscribe to timers and events prevents memory leaks. Let’s refactor the above example to clean after the timer:

import { useEffect, useState } from "react";

export default function App() {
  const [counter, setCounter] = useState(0);
  useEffect(() => {
    let timer;
    timer = setTimeout(() => {
      setCounter((prev) => prev + 1);
    }, 1000);

    return () => {
      clearTimeout(timer);
    };
  });
  return (
    <>
      <p>{counter}</p>
    </>
  );
}

Handling Events in Function Components

User-triggered events are a vital part of UI development. React provides a way to respond to user-triggered events through event handlersEvent handlers are functions passed as props to the React events and triggered in response to user interactions such as form submission, mouse movements, button clicks, input focus, etc. Earlier, we utilized two prevalent events in React, for instance, the onChange and onClick events. Additional commonly used events include:

  • onSubmit

  • onMouseOver

  • onMouseLeave

  • onMouseEnter

  • onKeyUp

  • onKeyDown

  • onKeyPress

  • onFocus

  • onBlur

Since we have used the onChange and onClick events in previous examples, the following example aims to create an event handler that responds to form submission:

import { useRef } from "react";

const App = () => {
  const inputRef = useRef(null);
  const submitHandler = (e) => {
    e.preventDefault();
    console.log(inputRef.current.value);
  };

  return (
    <>
      <form onSubmit={submitHandler}>
        <input ref={inputRef} type="text" name="" id="" />
        <button type="submit">Submit</button>
      </form>
    </>
  );
};

export default App;

https://res.cloudinary.com/da8vqkdmt/image/upload/v1705672432/prevent_default_axqjxy.gif

It is worth noting that the form submit event in the above example can only be triggered if an input or button has a submit type. Furthermore, the event method to prevent default behavior within the handler prevents from causing the page to reload.

Callbacks in React Function Components

A callback function is a function that handles logic in a parent component and is passed as props to a child function component. They foster parent-child communication and share information between components. The example below illustrates a callback function:

import { useState, useRef } from "react";

const FormContainer = ({ submit }) => {
  const inputRef = useRef(null);

  const submitHandler = (e) => {
    e.preventDefault();

    submit(inputRef.current.value);
  };
  return (
    <form onSubmit={submitHandler}>
      <input ref={inputRef} type="number" name="" id="" />
      <button type="submit">Submit</button>
    </form>
  );
};

const App = () => {
  const [count, setCount] = useState(0);
  const submitForm = (value) => {
    if (value) {
      setCount((prevCount) => prevCount + +value);
    }
  };

  return (
    <>
      <p>Count: {count}</p>
      <FormContainer submit={submitForm} />
    </>
  );
};

export default App;

Advantages of React Functional Components

React offers a lot of benefits, some of which include the following:

  • Streamlined handling of state and lifecycle events

  • Improved code maintenance and organization

  • Performance optimization

  • Encapsulation of composable and reusable logic

  • Simplified testing

Streamlined Handling of State and Lifecycle Events

The useState and useReducer Hook ensure a concise and efficient way of managing stateful logic in the function component. They enable the function component to keep track of data directly within the component using useState or through a reducer function using useReducer. The following code example refactors one of the examples above to show how to utilize useReducer to manage stateful logic efficiently:

import { useReducer } from "react";

const UPDATE_INPUT = "form-field/update";
const reducer = (state, action) => {
  if (action.type === UPDATE_INPUT) {
    return { ...state, [action.payload.name]: action.payload.value };
  }

  return state;
};

function TextInput({
  type = "",
  value = "",
  name = "",
  placeholder = "",
  onchange,
}) {
  return (
    <div style={{ padding: "0.5rem 0" }}>
      <input
        type={type}
        value={value}
        name={name}
        placeholder={placeholder}
        onChange={onchange}
      />
    </div>
  );
}

export default function App() {
  const [state, dispatch] = useReducer(reducer, { name: "", email: "" });

  const changeHandler = (e) => {
    const name = e.target.name;
    const value = e.target.value;
    dispatch({ type: UPDATE_INPUT, payload: { name, value } });
  };

  const submit = () => {
    console.log({ state });
  };

  return (
    <>
      <TextInput
        type="text"
        value={state.name}
        name="name"
        placeholder="Enter name..."
        onchange={changeHandler}
      />
      <TextInput
        type="email"
        value={state.email}
        name="email"
        placeholder="Enter email..."
        onchange={changeHandler}
      />
      <button type="button" onClick={submit}>
        Submit
      </button>
    </>
  );
}

https://res.cloudinary.com/da8vqkdmt/image/upload/v1705613838/useReducer_igumel.gif
On the other hand, the useEffect Hook ensures efficient handling of side effects in function components. It enables a streamlined connection to an external system, simplifying the handling of asynchronous tasks, data fetching, and subscriptions. The following is a simple example of applying the useEffect Hook:

import { useEffect } from "react";
export default function App() {
  useEffect(() => {
    (async function() {
      // Perform some asynchronous API request
    })()
  
  }, []);
  return <></>;
}

Improved Code Maintenance and Organization

Hooks increase the modularity and ease of reasoning in a code, allowing developers to organize their code based on logic rather than lifecycle methods. This approach enhances the codebase’s overall clarity, simplicity, and maintenance.

Enhanced Performance

Hooks minimize the amount of unnecessary re-render after each interaction. By utilizing the useMemo and useCallback Hooks, developers can ensure the memoization of values and functions respectively, preventing unnecessary re-renders and improving performance.

Encapsulation of Composable and Reusable Logic

React provides a way to encapsulate and reuse logic that allows us to share specific functionalities with several components through custom HooksCustom Hooks are functions that integrate one or more React built-in Hooks to achieve specific functionality. A custom Hook’s function name begins with the prefix ‘use.’ By employing custom Hooks, developers can prevent code duplication and promote reusability.

Simplified Testing

At the base level, react functional components are pure functions that lack side effects or state. Their straightforward structure makes it easy to test them individually without mocking or dealing with lifecycle methods.

Best Practices for React Functional Component

When working with React functional components it is vital to write cleaner, more maintainable, and performant code. Consider the following recommended best practices for React functional components:

  • Ensure component modularity

  • Use Hooks wisely

  • Avoid using anonymous functions when rendering

  • Consider using React memo for expensive computation

  • Organize stateful logic

Ensure Component Modularity

Break down complex UI features into smaller components that handle specific features or behavior. This modular approach promotes reusability, readability, and ease of maintenance.

Use Hooks Wisely

When using Hooks that receive a callback as an argument in functional components, be sure to provide a dependencies array when necessary.

Avoid Using Anonymous Functions When Rendering

Refrain from using anonymous functions directly within the return statement of a function component, similar to the render method in class components, especially for event handlers. By defining functions outside the render method, you can encapsulate them within the useCallback Hook, thereby preventing unnecessary re-initialization of functions with each render. This practice improves the performance and maintainability of a React functional component.

Consider Using React Memo for Expensive Computation

When dealing with expensive state and props calculation, use the memo API to prevent needless re-rendering of function components.

Organize Stateful Logic

When multiple functional components depend on the same state variables or involve complex state logic, it is advisable to organize this logic into a custom Hook. This strategy promotes code reusability and maintains a cleaner component structure.

Final Thoughts

This article emphasizes the preference for functional components when building modern React applications. Functional components provide numerous advantages, including simplifying complex logic, enhancing code maintainability and efficiency, and optimizing performance. Consequently, React function components are increasingly gaining prominence in contemporary React development due to their versatility and reliability.

In light of these advantages, visit Purecode AI to explore our library of UI components. In addition, you can also generate custom UI components that are specific to your project’s requirements with AI.

Additional Resources

If you enjoyed reading this article, check out other articles from our blog to improve your React knowledge:

Ofili Chukwuemeka Timothy

Ofili Chukwuemeka Timothy