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 andcreateList
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:
Event Handlers: Suitable for actions triggered by user interactions, such as updating state or making API calls.
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.