Understanding Render Logic, Event Handlers, and Side Effects in React

Understanding Render Logic, Event Handlers, and Side Effects in React

When building applications with React, one of the fundamental concepts to understand is the difference between render logic and event handlers, as well as how they relate to side effects. These concepts are very important for writing efficient and bug-free components. In this article, we’ll explore these ideas in detail, with practical code examples to solidify our understanding.

What is Render Logic?

Render logic refers to the code in React components that determines how a component is displayed on the screen. It executes every time a component renders and includes:

  • Top-level component code: Variables or functions declared in the component's main body.

  • The return block: JSX that defines the component's visual structure.

  • Helper functions: Functions invoked within the return block that contribute to rendering the view.

Here’s a code that demonstrates render logic in action.

function ItemList() {
  const items = ['Item 1', 'Item 2', 'Item 3'];

  // Helper function to generate list items
  function createList() {
    return items.map((item, index) => <li key={index}>{item}</li>);
  }

  // Render logic includes the JSX and helper function call
  return (
    <ul>
      {createList()}
    </ul>
  );
}
  • In the example above, the items array and createList function reside at the component's top level, making them part of the render logic.

  • The return block describes how the component should be displayed.

What are Event Handlers?

Event handlers are functions that execute in response to user interactions or events. Unlike render logic, event handlers don’t determine how the component is displayed. Instead, they handle interactions, such as updating state, making API calls, or performing other actions that change the application's state.

Example of an Event Handler

function TextInput() {
  const [text, setText] = React.useState('');

  function handleInputChange(event) {
    setText(event.target.value); // Updates state with user input
  }

  return (
    <input 
      type="text" 
      value={text} 
      onChange={handleInputChange} 
    />
  );
}

In the above code example, the handleInputChange function is triggered whenever the input field changes, making it an event handler.

Functional Programming in React: Side Effects and Pure Functions

What Are Side Effects?

A side effect occurs when a function:

  • Depends on data outside its scope.

  • Modifies external data or interacts with the outside world.

Common side effects in programming include:

  • Making API requests.

  • Writing to the DOM.

  • Setting timers.

What Are Pure Functions?

A pure function:

  • Produces the same output for the same input.

  • Has no side effects, meaning it doesn’t interact with or modify anything outside its scope.

Examples

Pure Function:

function calculateArea(radius) {
  return Math.PI * radius * radius;
}

Impure Function:

let counter = 0;

function incrementCounter() {
  counter += 1; // Modifies an external variable
  return counter;
}

Why Render Logic Must Be Pure

React enforces purity in render logic for consistency and performance:

  • A component must always return the same JSX output for the same input (props).

  • Actions not allowed in render logic:

    • API calls.

    • DOM manipulations.

    • State updates (to prevent infinite loops).

Example of Violating Purity

function ImpureComponent(props) {
  props.value = 10; // Mutating props (not allowed)

  return <p>{props.value}</p>;
}

This example is problematic because mutating props introduces a side effect, which is prohibited in render logic.

Where to Place Side Effects in React

While render logic must remain pure, side effects are necessary for building interactive applications. React provides appropriate places for side effects:

  1. Event Handlers: Suitable for actions triggered by user interactions, such as updating state or making API calls.

  2. useEffect Hook: Ideal for side effects that occur when a component is mounted, updated, or unmounted.

Example: Using useEffect for Side Effects

import React, { useState, useEffect } from 'react';

function FetchDataComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('/api/data')
      .then(response => response.json())
      .then(data => setData(data)); // Updates state with fetched data
  }, []); // Runs only when the component mounts

  return <div>{data ? data.title : 'Loading...'}</div>;
}

In this example, the useEffect hook handles the side effect of fetching data from an API.

Conclusion

In this article, we’ve seen that Render logic defines how a component is displayed and must be pure to ensure consistent and predictable behavior. We also saw that event handlers handle interactions and allow side effects like state updates and API calls, as well as how to use the useEffect hook to handle side effects during a component’s lifecycle. By adhering to these principles, you can create React components that are efficient and easy to maintain.

PS: This article is as a result of my learning from Jonas Schmedtmann React’s course.