React Interview Questions

50+ React interview questions and answers in quiz-style format, answered by ex-FAANG interviewers
Solved by ex-interviewers
Covers critical topics

In real-world scenarios, mastering React goes far beyond just building components. It’s about creating efficient, reusable, and performant applications. React interviewers typically focus on key areas such as:

  • Component Lifecycle: Understanding how components mount, update, and unmount is crucial for managing UI and state.
  • State and Props Management: Knowing when and how to use props, state, and context to share data across components.
  • Hooks: Leveraging React hooks like useState, useEffect, and useReducer to simplify logic and manage side effects.
  • Performance Optimization: Efficient rendering, memoization, and handling large datasets.
  • Testing: Writing robust tests for React components using tools like Jest and React Testing Library.
  • Routing: Managing views and navigation in single-page applications with React Router.

Below, you’ll find 50+ expertly curated questions covering everything from component lifecycle and state management to hooks and performance optimization. Each question includes:

  • Quick answers (TL;DR): Clear, concise responses to help you answer confidently.
  • In-depth explanations: Detailed insights to ensure you fully understand each concept.

Best of all, our list is crafted by senior and staff engineers from top tech companies, not unverified or AI-generated content. Don’t waste time—prepare with real, experienced-backed React interview questions!

If you're looking for React coding questions -We've got you covered as well, with:
Javascript coding
  • 90+ React coding interview questions
  • An in-browser coding workspace that mimics real interview conditions
  • Reference solutions from ex-interviewers at Big Tech companies
  • Instant UI preview for UI questions
Get Started
Join 50,000+ engineers

What is the difference between state and props in React?

Topics
React

TL;DR

In React, state is a local data storage that is managed within a component and can change over time, while props are read-only attributes passed from a parent component to a child component. State is used for data that changes within a component, whereas props are used to pass data and event handlers to child components.


What is the difference between state and props in React?

State

State is a built-in object in React components that holds data or information about the component. It is managed within the component and can change over time, usually in response to user actions or network responses. When the state changes, the component re-renders to reflect the new state.

  • State is local to the component and cannot be accessed or modified outside of it
  • State can be initialized in the constructor of a class component or using the useState hook in a functional component
  • State changes are asynchronous and should be done using the setState method in class components or the updater function returned by useState in functional components

Example of state in a class component:

class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}

Example of state in a functional component:

import React, { useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

Props

Props (short for properties) are read-only attributes passed from a parent component to a child component. They are used to pass data and event handlers down the component tree. Props are immutable, meaning they cannot be changed by the child component.

  • Props are passed to the child component as an argument to the component function or as a property on the this object in class components
  • Props allow components to be reusable by passing different data to them
  • Props can be used to pass functions as well, enabling child components to communicate with parent components

Example of props in a class component:

class ParentComponent extends React.Component {
render() {
return <ChildComponent message="Hello, World!" />;
}
}
class ChildComponent extends React.Component {
render() {
return <p>{this.props.message}</p>;
}
}

Example of props in a functional component:

function ParentComponent() {
return <ChildComponent message="Hello, World!" />;
}
function ChildComponent(props) {
return <p>{props.message}</p>;
}

Key differences

  • State is managed within the component, while props are passed from a parent component
  • State can change over time, while props are immutable
  • State is used for data that changes within a component, while props are used to pass data and event handlers to child components

Further reading

What is React? Describe the benefits of React

Topics
React

TL;DR

React is a JavaScript library created by Facebook for building user interfaces, primarily for single-page applications. It allows developers to create reusable components that manage their own state. Key benefits of React include a component-based architecture for modular code, the virtual DOM for efficient updates, a declarative UI for more readable code, one-way data binding for predictable data flow, and a strong community and ecosystem with abundant resources and tools.

Key characteristics of React:

  • Declarative: You describe the desired state of your UI based on data, and React handles updating the actual DOM efficiently.
  • Component-based: Build reusable and modular UI elements (components) that manage their own state and logic.
  • Virtual DOM: React uses a lightweight in-memory representation of the actual DOM, allowing it to perform updates selectively and efficiently.
  • JSX: While not mandatory, JSX provides a syntax extension that allows you to write HTML-like structures within your JavaScript code, making UI development more intuitive.

What is React?

React is an open-source JavaScript library developed by Facebook for building user interfaces. It focuses on the view layer of an application and is especially useful for creating single-page applications where a seamless user experience is crucial. React allows developers to build encapsulated components that manage their own state and compose them to create complex UIs.

Benefits of React

1. Component-based architecture

React encourages breaking down your UI into independent, reusable components. Each component encapsulates its own state, logic, and rendering, making your code:

  • Modular and reusable: Components can be easily reused across different parts of your application or even in other projects.
  • Maintainable: Changes within a component are isolated, reducing the risk of unintended side effects.
  • Easier to test: Components can be tested independently, ensuring their functionality and reliability.

2. Virtual DOM and efficient updates

React utilizes a virtual DOM, a lightweight in-memory representation of the actual DOM. When data changes, React first updates the virtual DOM, then compares it to the previous version. This process, known as diffing, allows React to identify the minimal set of changes required in the actual DOM. By updating only the necessary elements, React minimizes expensive DOM manipulations, resulting in significant performance improvements.

3. Large and active community

React boasts a vast and active community of developers worldwide. This translates to:

  • Extensive documentation and resources: Find comprehensive documentation, tutorials, and community-driven resources to aid your learning and development process.
  • Abundant third-party libraries and tools: Leverage a rich ecosystem of pre-built components, libraries, and tools that extend React's functionality and streamline development.
  • Strong community support: Seek help, share knowledge, and engage with fellow developers through forums, online communities, and meetups.

4. Learn once, write anywhere

React's versatility extends beyond web development. With React Native, you can use your React knowledge to build native mobile applications for iOS and Android. This "learn once, write anywhere" approach allows you to:

  • Share code between platforms: Reuse components and logic across web and mobile, reducing development time and effort.
  • Leverage existing skills: Apply your React expertise to mobile development without needing to learn entirely new technologies.
  • Target multiple platforms with a single codebase: Streamline development and maintenance by managing a single codebase for multiple platforms.

Further reading

What is the difference between React Node, React Element, and a React Component?

Topics
React

TL;DR

A React Node is any renderable unit in React, such as an element, string, number, or null. A React Element is an immutable object describing what to render, created using JSX or React.createElement. A React Component is a function or class that returns React Elements, enabling the creation of reusable UI pieces.


React node

A React Node is the most basic unit in the React rendering system. It can be a React element, a string, a number, a boolean, or null. Essentially, anything that can be rendered in React is a React Node.

const stringNode = 'Hello, world!';
const numberNode = 123;
const booleanNode = true;
const nullNode = null;
const elementNode = <div>Hello, world!</div>;

React element

A React Element is an immutable, plain object representing what you want to see on the screen. It includes the type (such as a string for HTML tags or a React component), props, and children. React elements are created using JSX syntax or React.createElement.

const element = <div className="greeting">Hello, world!</div>;
// Using React.createElement
const element = React.createElement(
'div',
{ className: 'greeting' },
'Hello, world!',
);

React component

A React Component is a reusable piece of the UI that can accept inputs (props) and returns React elements describing the UI. There are two types of components: function components and class components.

  • Function components: These are simpler and are just functions that take props as an argument and return a React element.

    function Welcome(props) {
    return <h1>Hello, {props.name}</h1>;
    }
  • Class components: These are ES6 classes that extend React.Component and must have a render method returning a React element.

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

Further reading

What is JSX and how does it work?

Topics
React

TL;DR

JSX stands for JavaScript XML. It is a syntax extension for JavaScript that allows you to write HTML-like code within JavaScript. JSX makes it easier to create React components by allowing you to write what looks like HTML directly in your JavaScript code. Under the hood, JSX is transformed into JavaScript function calls, typically using a tool like Babel. For example, <div>Hello, world!</div> in JSX is transformed into React.createElement('div', null, 'Hello, world!').


What is JSX and how does it work?

What is JSX?

JSX stands for JavaScript XML. It is a syntax extension for JavaScript that allows you to write HTML-like code within JavaScript. JSX is primarily used with React to describe what the UI should look like.

How does JSX work?

JSX is not valid JavaScript by itself. It needs to be transformed into regular JavaScript before it can be executed by the browser. This transformation is usually done by a tool like Babel.

JSX syntax

JSX allows you to write HTML-like tags directly in your JavaScript code. For example:

const element = <h1>Hello, world!</h1>;

Transformation process

When you write JSX, it is transformed into JavaScript function calls. For example, the JSX code:

const element = <h1>Hello, world!</h1>;

is transformed into:

const element = React.createElement('h1', null, 'Hello, world!');

Embedding expressions

You can embed JavaScript expressions inside JSX using curly braces {}. For example:

const name = 'John';
const element = <h1>Hello, {name}!</h1>;

Attributes in JSX

You can use quotes to specify string literals as attributes and curly braces to embed JavaScript expressions. For example:

const element = <img src={user.avatarUrl} alt="User Avatar" />;

JSX is an expression

After compilation, JSX expressions become regular JavaScript function calls and evaluate to JavaScript objects. This means you can use JSX inside if statements, for loops, and assign it to variables.

JSX prevents injection attacks

By default, React DOM escapes any values embedded in JSX before rendering them. This ensures that you can never inject anything that's not explicitly written in your application.

Further reading

What is the purpose of the `key` prop in React?

Topics
React

TL;DR

The key prop in React is used to uniquely identify elements in a list. It helps React optimize rendering by efficiently updating and reordering elements. Without a unique key, React may re-render elements unnecessarily, leading to performance issues and bugs.

{
items.map((item) => <ListItem key={item.id} value={item.value} />);
}

What is the purpose of the key prop in React?

Introduction

The key prop is a special attribute you need to include when creating lists of elements in React. It is crucial for helping React identify which items have changed, been added, or removed, thereby optimizing the rendering process.

Why key is important

  1. Efficient updates: React uses the key prop to keep track of elements. When the state of a list changes, React can quickly determine which items need to be re-rendered, added, or removed.
  2. Avoiding bugs: Without unique keys, React may re-render elements unnecessarily or incorrectly, leading to potential bugs in your application.
  3. Performance optimization: By using unique keys, React minimizes the number of DOM operations, making your application faster and more efficient.

How to use the key prop

When rendering a list of elements, you should provide a unique key for each element. This key should be stable, meaning it should not change between renders. Typically, you can use a unique identifier from your data, such as an id.

const items = [
{ id: 1, value: 'Item 1' },
{ id: 2, value: 'Item 2' },
{ id: 3, value: 'Item 3' },
];
function ItemList() {
return (
<ul>
{items.map((item) => (
<ListItem key={item.id} value={item.value} />
))}
</ul>
);
}
function ListItem({ value }) {
return <li>{value}</li>;
}

Common mistakes

  1. Using array index as key: While it might be tempting to use the array index as the key, this is not recommended because the index can change if the list is reordered or items are added/removed.
  2. Non-unique keys: Ensure that the keys are unique across the list. Duplicate keys can lead to unexpected behavior and bugs.
// Bad practice: using array index as key
{
items.map((item, index) => <ListItem key={index} value={item.value} />);
}
// Good practice: using a unique identifier as key
{
items.map((item) => <ListItem key={item.id} value={item.value} />);
}

Further reading

What is the consequence of using array indices as the value for `key`s in React?

Topics
React

TL;DR

Using array indices as the value for keys in React can lead to performance issues and bugs. When the order of items changes, React may not correctly identify which items have changed, leading to unnecessary re-renders or incorrect component updates. It's better to use unique identifiers for keys to ensure React can efficiently manage and update the DOM.


Consequence of using array indices as the value for keys in React

Performance issues

When array indices are used as keys, React may not efficiently update the DOM. If the order of items in the array changes, React will not be able to correctly identify which items have been added, removed, or moved. This can lead to unnecessary re-renders and decreased performance.

Incorrect component updates

Using array indices as keys can cause bugs in your application. For example, if the order of items changes, React may reuse the same component instances for different items, leading to incorrect state and props being passed to components. This can result in unexpected behavior and hard-to-debug issues.

Example

Consider the following example where array indices are used as keys:

const items = ['Item 1', 'Item 2', 'Item 3'];
const List = () => (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);

If the order of items changes, React may not correctly update the DOM, leading to performance issues and potential bugs.

Better approach

Instead of using array indices, use unique identifiers for keys. This ensures that React can efficiently manage and update the DOM.

const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
];
const List = () => (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);

In this example, using item.id as the key ensures that React can correctly identify and update items when the order changes.

Further reading

What is the difference between controlled and uncontrolled React Components?

Topics
React

TL;DR

Controlled components in React are those where the form data is handled by the component's state. The state is the single source of truth, and any changes to the form input are managed through event handlers. Uncontrolled components, on the other hand, store their own state internally and rely on refs to access the form values. Controlled components offer more control and are easier to test, while uncontrolled components can be simpler to implement for basic use cases.


What is the difference between controlled and uncontrolled React components?

Controlled components

Controlled components are those where the form data is handled by the component's state. The state is the single source of truth, and any changes to the form input are managed through event handlers.

Example
class ControlledComponent extends React.Component {
constructor(props) {
super(props);
this.state = { value: '' };
}
handleChange = (event) => {
this.setState({ value: event.target.value });
};
handleSubmit = (event) => {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}

Uncontrolled components

Uncontrolled components store their own state internally and rely on refs to access the form values. This approach is more similar to traditional HTML form elements.

Example
class UncontrolledComponent extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
handleSubmit = (event) => {
alert('A name was submitted: ' + this.inputRef.current.value);
event.preventDefault();
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.inputRef} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}

Key differences

State management
  • Controlled components: The state is managed by the React component.
  • Uncontrolled components: The state is managed by the DOM.
Data flow
  • Controlled components: Data flows from the component's state to the input element.
  • Uncontrolled components: Data flows from the input element to the component via refs.
Use cases
  • Controlled components: Preferred when you need to enforce validation, conditionally disable/enable inputs, or perform other complex interactions.
  • Uncontrolled components: Useful for simple forms or when you need to integrate with non-React code.

Further reading

What are some pitfalls about using context in React?

Topics
React

TL;DR

Using context in React can lead to performance issues if not managed properly. It can cause unnecessary re-renders of components that consume the context, even if the part of the context they use hasn't changed. Additionally, overusing context for state management can make the code harder to understand and maintain. It's important to use context sparingly and consider other state management solutions like Redux or Zustand for more complex state needs.


Pitfalls about using context in React

Performance issues

One of the main pitfalls of using context in React is the potential for performance issues. When the context value changes, all components that consume the context will re-render, even if they don't use the part of the context that changed. This can lead to unnecessary re-renders and degrade the performance of your application.

Example
const MyContext = React.createContext();
function ParentComponent() {
const [value, setValue] = React.useState(0);
return (
<MyContext.Provider value={value}>
<ChildComponent />
</MyContext.Provider>
);
}
function ChildComponent() {
const value = React.useContext(MyContext);
console.log('ChildComponent re-rendered');
return <div>{value}</div>;
}

In this example, ChildComponent will re-render every time the value in ParentComponent changes, even if ChildComponent doesn't need to update.

Overusing context

Using context for state management can make the code harder to understand and maintain. Context is best suited for global state that doesn't change frequently, such as theme settings or user authentication status. Overusing context for more complex state management can lead to a tangled and hard-to-follow codebase.

Debugging difficulties

Debugging issues related to context can be challenging. Since context updates can trigger re-renders in multiple components, it can be difficult to trace the source of a bug or performance issue. This is especially true in larger applications with many context providers and consumers.

Lack of fine-grained control

Context provides a way to pass data through the component tree without having to pass props down manually at every level. However, it lacks fine-grained control over which components should re-render when the context value changes. This can lead to performance bottlenecks if not managed carefully.

Alternatives to context

For more complex state management needs, consider using other state management solutions like Redux, Zustand, or Recoil. These libraries provide more fine-grained control over state updates and can help avoid some of the pitfalls associated with using context.

Further reading

What are the benefits of using hooks in React?

Topics
React

TL;DR

Hooks in React allow you to use state and other React features without writing a class. They make it easier to reuse stateful logic between components, improve code readability, and simplify the codebase by reducing the need for lifecycle methods. Hooks like useState and useEffect are commonly used to manage state and side effects in functional components.


Benefits of using hooks in React

Simplified state management

Hooks like useState allow you to add state to functional components without converting them to class components. This makes the code more concise and easier to read.

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

Improved code readability

Hooks help in breaking down complex components into smaller, reusable pieces of logic. This makes the code more modular and easier to understand.

Reusable logic

Custom hooks allow you to extract and reuse stateful logic across multiple components. This promotes code reuse and reduces duplication.

function useCustomHook() {
const [state, setState] = useState(initialState);
// Custom logic here
return [state, setState];
}

Reduced need for lifecycle methods

Hooks like useEffect can replace lifecycle methods such as componentDidMount, componentDidUpdate, and componentWillUnmount. This simplifies the component lifecycle management.

useEffect(() => {
// Side effect logic here
return () => {
// Cleanup logic here
};
}, [dependencies]);

Better separation of concerns

Hooks allow you to separate concerns by grouping related logic together. This makes the codebase more maintainable and easier to debug.

Enhanced testing

Functional components with hooks are generally easier to test compared to class components. Hooks can be tested in isolation, making unit tests more straightforward.

Further reading

What are the rules of React hooks?

Topics
React

TL;DR

React hooks have a few essential rules to ensure they work correctly. Always call hooks at the top level of your React function, never inside loops, conditions, or nested functions. Only call hooks from React function components or custom hooks. These rules ensure that hooks maintain the correct state and lifecycle behavior.


What are the rules of React hooks?

Always call hooks at the top level

Hooks should always be called at the top level of your React function. This means you should not call hooks inside loops, conditions, or nested functions. This rule ensures that hooks are called in the same order each time a component renders, which is crucial for maintaining the correct state and lifecycle behavior.

// Correct
function MyComponent() {
const [count, setCount] = useState(0);
if (count > 0) {
// Do something
}
}
// Incorrect
function MyComponent() {
if (someCondition) {
const [count, setCount] = useState(0); // This will break the rules of hooks
}
}

Only call hooks from React function components or custom hooks

Hooks should only be called from React function components or custom hooks. This rule ensures that hooks are used in the appropriate context where React can manage their state and lifecycle.

// Correct
function MyComponent() {
const [count, setCount] = useState(0);
return <div>{count}</div>;
}
// Correct (custom hook)
function useCustomHook() {
const [state, setState] = useState(null);
return [state, setState];
}
// Incorrect
function regularFunction() {
const [count, setCount] = useState(0); // This will break the rules of hooks
}

Use the eslint-plugin-react-hooks linter

To enforce these rules, you can use the eslint-plugin-react-hooks linter. This plugin will help you identify and fix violations of the rules of hooks in your code.

npm install eslint-plugin-react-hooks --save-dev

Add the plugin to your ESLint configuration:

{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error", // Checks rules of hooks
"react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
}
}

Further reading

What is the difference between `useEffect` and `useLayoutEffect` in React?

Topics
React

TL;DR

useEffect and useLayoutEffect are React hooks used to handle side effects in functional components, but they differ in timing and use cases:

  • useEffect: Runs asynchronously after the DOM has been painted. It is suitable for tasks like data fetching, subscriptions, or logging.
  • useLayoutEffect: Runs synchronously after DOM mutations but before the browser paints. Use it for tasks like measuring DOM elements or synchronizing the UI with the DOM.

Code example:

import React, { useEffect, useLayoutEffect, useRef } from 'react';
function Example() {
const ref = useRef();
useEffect(() => {
console.log('useEffect: Runs after DOM paint');
});
useLayoutEffect(() => {
console.log('useLayoutEffect: Runs before DOM paint');
console.log('Element width:', ref.current.offsetWidth);
});
return <div ref={ref}>Hello</div>;
}

What is useEffect?

useEffect is a React hook used for managing side effects in functional components. Side effects include operations like fetching data, updating a subscription, or interacting with the browser's DOM API.

  • It runs asynchronously after the DOM has been updated and painted.
  • It does not block the browser from updating the UI.
  • By default, it runs after every render, but dependencies can control its execution.

Code example

import React, { useEffect } from 'react';
function Example() {
useEffect(() => {
console.log('Component mounted or updated');
return () => console.log('Cleanup on unmount or dependency change');
}, []); // Runs only on mount and unmount
return <div>Hello, World!</div>;
}

Common use cases

  • Fetching data from an API
  • Setting up subscriptions (e.g., WebSocket connections)
  • Logging or analytics tracking
  • Adding and removing event listeners

What is useLayoutEffect?

useLayoutEffect is a React hook similar to useEffect, but it differs in timing. It runs synchronously after DOM mutations and before the browser paints the screen.

  • It is suitable for tasks where the DOM needs to be accessed before the paint.
  • It can block rendering, so it should be used sparingly.

Code example

import React, { useLayoutEffect, useRef } from 'react';
function Example() {
const ref = useRef();
useLayoutEffect(() => {
console.log('Element dimensions:', ref.current.getBoundingClientRect());
});
return <div ref={ref}>Hello</div>;
}

Common use cases

  • Measuring DOM elements (e.g., for animations or layouts)
  • Adjusting DOM properties or styles based on calculations
  • Fixing UI synchronization issues

key differences between useEffect and useLayoutEffect

Timing

  • useEffect: Executes after the browser has painted the UI.
  • useLayoutEffect: Executes before the browser paints, right after DOM changes.

Blocking behavior

  • useEffect: Non-blocking, runs asynchronously.
  • useLayoutEffect: Blocking, runs synchronously.

Use case examples

  • useEffect: Fetching data, updating state, or adding event listeners.
  • useLayoutEffect: Measuring DOM elements, managing animations, or solving layout issues.

Further reading

What is the purpose of callback function argument format of `setState()` in React and when should it be used?

Topics
React

TL;DR

The callback function argument format of setState() in React is used to ensure that state updates are based on the most recent state and props. This is particularly important when the new state depends on the previous state. Instead of passing an object directly to setState(), you pass a function that takes the previous state and props as arguments and returns the new state.

this.setState((prevState, props) => ({
counter: prevState.counter + props.increment,
}));

Purpose of callback function argument format of setState() in React

Ensuring state updates are based on the most recent state

React's setState() is asynchronous, meaning multiple calls to setState() can be batched together for performance reasons. If you rely on the current state to compute the next state, using the callback function format ensures that you are working with the most up-to-date state.

Syntax

The callback function format of setState() takes a function as an argument. This function receives two parameters: prevState and props. It returns an object representing the new state.

this.setState((prevState, props) => {
return {
counter: prevState.counter + props.increment,
};
});

When to use it

  • When the new state depends on the previous state: If you need to update the state based on the current state, use the callback function format to avoid potential issues with asynchronous state updates.
  • When multiple state updates are batched: In scenarios where multiple setState() calls might be batched together, using the callback function ensures that each update is based on the most recent state.

Example

Consider a counter component where the state update depends on the previous state:

class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
}
incrementCounter = () => {
this.setState((prevState, props) => ({
counter: prevState.counter + 1,
}));
};
render() {
return (
<div>
<p>Counter: {this.state.counter}</p>
<button onClick={this.incrementCounter}>Increment</button>
</div>
);
}
}

In this example, using the callback function format ensures that the counter state is correctly incremented, even if multiple incrementCounter calls are made in quick succession.

Further reading

What does the dependency array of `useEffect` affect?

Topics
React

TL;DR

The dependency array of useEffect determines when the effect should re-run. If the array is empty, the effect runs only once after the initial render. If it contains variables, the effect runs whenever any of those variables change. If omitted, the effect runs after every render.


What does the dependency array of useEffect affect?

Introduction to useEffect

The useEffect hook in React is used to perform side effects in functional components. These side effects can include data fetching, subscriptions, or manually changing the DOM. The useEffect hook takes two arguments: a function that contains the side effect logic and an optional dependency array.

Dependency array

The dependency array is the second argument to the useEffect hook. It is an array of values that the effect depends on. React uses this array to determine when to re-run the effect.

useEffect(() => {
// Side effect logic here
}, [dependency1, dependency2]);

How the dependency array affects useEffect

  1. Empty dependency array ([]):

    • The effect runs only once after the initial render.
    • This is similar to the behavior of componentDidMount in class components.
    useEffect(() => {
    // This code runs only once after the initial render
    }, []);
  2. Dependency array with variables:

    • The effect runs after the initial render and whenever any of the specified dependencies change.
    • React performs a shallow comparison of the dependencies to determine if they have changed.
    useEffect(() => {
    // This code runs after the initial render and whenever dependency1 or dependency2 changes
    }, [dependency1, dependency2]);
  3. No dependency array:

    • The effect runs after every render.
    • This can lead to performance issues if the effect is expensive.
    useEffect(() => {
    // This code runs after every render
    });

Common pitfalls

  1. Stale closures:

    • If you use state or props inside the effect without including them in the dependency array, you might end up with stale values.
    • Always include all state and props that the effect depends on in the dependency array.
    const [count, setCount] = useState(0);
    useEffect(() => {
    const handle = setInterval(() => {
    console.log(count); // This might log stale values if `count` is not in the dependency array
    }, 1000);
    return () => clearInterval(handle);
    }, [count]); // Ensure `count` is included in the dependency array
  2. Functions as dependencies:

    • Functions are recreated on every render, so including them in the dependency array can cause the effect to run more often than necessary.
    • Use useCallback to memoize functions if they need to be included in the dependency array.
    const handleClick = useCallback(() => {
    // Handle click
    }, []);
    useEffect(() => {
    // This effect will not re-run unnecessarily because `handleClick` is memoized
    }, [handleClick]);

Further reading

What is the `useRef` hook in React and when should it be used?

Topics
React

TL;DR

The useRef hook in React is used to create a mutable object that persists across renders. It can be used to access and manipulate DOM elements directly, store mutable values that do not cause re-renders when updated, and keep a reference to a value without triggering a re-render. For example, you can use useRef to focus an input element:

import React, { useRef, useEffect } from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null);
useEffect(() => {
inputEl.current.focus();
}, []);
return <input ref={inputEl} type="text" />;
}

What is the useRef hook in React and when should it be used?

Introduction to useRef

The useRef hook in React is a function that returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

Key use cases for useRef

Accessing and manipulating DOM elements

One of the primary use cases for useRef is to directly access and manipulate DOM elements. This is particularly useful when you need to interact with the DOM in ways that are not easily achievable through React's declarative approach.

Example:

import React, { useRef, useEffect } from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null);
useEffect(() => {
inputEl.current.focus();
}, []);
return <input ref={inputEl} type="text" />;
}

In this example, the useRef hook is used to create a reference to the input element, and the useEffect hook is used to focus the input element when the component mounts.

Storing mutable values

useRef can also be used to store mutable values that do not cause a re-render when updated. This is useful for keeping track of values that change over time but do not need to trigger a re-render.

Example:

import React, { useRef } from 'react';
function Timer() {
const count = useRef(0);
const increment = () => {
count.current += 1;
console.log(count.current);
};
return <button onClick={increment}>Increment</button>;
}

In this example, the count variable is stored in a useRef object, and its value is incremented without causing the component to re-render.

Keeping a reference to a value

useRef can be used to keep a reference to a value without triggering a re-render. This is useful for storing values that need to persist across renders but do not need to cause a re-render when they change.

Example:

import React, { useRef, useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
}, [count]);
return (
<div>
<h1>Now: {count}</h1>
<h2>Before: {prevCountRef.current}</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

In this example, prevCountRef is used to keep a reference to the previous value of count without causing a re-render.

Further reading

What is the `useCallback` hook in React and when should it be used?

Topics
React

TL;DR

The useCallback hook in React is used to memoize functions, preventing them from being recreated on every render. This is particularly useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders. You should use useCallback when you have a function that is passed as a prop to a child component and you want to avoid the child component re-rendering unnecessarily.

const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);

What is the useCallback hook in React and when should it be used?

What is useCallback?

The useCallback hook is a React hook that returns a memoized version of the callback function that only changes if one of the dependencies has changed. It is useful for optimizing performance by preventing unnecessary re-creations of functions.

Syntax

const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);

When should useCallback be used?

Preventing unnecessary re-renders

When you pass a function as a prop to a child component, the child component may re-render every time the parent component re-renders, even if the function itself hasn't changed. Using useCallback ensures that the function reference remains the same as long as its dependencies haven't changed, thus preventing unnecessary re-renders.

const ParentComponent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return <ChildComponent onClick={handleClick} />;
};
const ChildComponent = React.memo(({ onClick }) => {
console.log('ChildComponent rendered');
return <button onClick={onClick}>Click me</button>;
});
Optimizing performance

In complex applications, re-creating functions on every render can be costly in terms of performance. By using useCallback, you can avoid this overhead and make your application more efficient.

Caveats

  • Overuse: Overusing useCallback can lead to more complex code and may not always result in performance improvements. It should be used judiciously.
  • Dependencies: Ensure that all dependencies are correctly specified in the dependency array. Missing dependencies can lead to stale closures and bugs.

Further reading

What is the `useMemo` hook in React and when should it be used?

Topics
React

TL;DR

The useMemo hook in React is used to memoize expensive calculations so that they are only recomputed when one of the dependencies has changed. This can improve performance by avoiding unnecessary recalculations. You should use useMemo when you have a computationally expensive function that doesn't need to run on every render.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

What is the useMemo hook in React and when should it be used?

What is useMemo?

The useMemo hook is a built-in React hook that allows you to memoize the result of a function. This means that the function will only be re-executed when one of its dependencies changes. The primary purpose of useMemo is to optimize performance by preventing unnecessary recalculations.

Syntax

The syntax for useMemo is as follows:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • The first argument is a function that returns the value you want to memoize.
  • The second argument is an array of dependencies. The memoized value will only be recomputed when one of these dependencies changes.

When should it be used?

Expensive calculations

If you have a function that performs a computationally expensive calculation, you can use useMemo to ensure that this calculation is only performed when necessary.

const expensiveCalculation = (num) => {
// Some expensive calculation
return num * 2;
};
const MyComponent = ({ number }) => {
const memoizedValue = useMemo(() => expensiveCalculation(number), [number]);
return <div>{memoizedValue}</div>;
};
Avoiding unnecessary renders

useMemo can also be useful for avoiding unnecessary renders of child components. If a child component depends on a value that is expensive to compute, you can use useMemo to ensure that the value is only recomputed when necessary.

const MyComponent = ({ items }) => {
const sortedItems = useMemo(() => {
return items.sort((a, b) => a - b);
}, [items]);
return <ChildComponent sortedItems={sortedItems} />;
};

Caveats

  • Overuse: Overusing useMemo can lead to more complex code without significant performance benefits. It should be used judiciously.
  • Dependencies: Make sure to correctly specify all dependencies. Missing dependencies can lead to stale values, while extra dependencies can lead to unnecessary recalculations.

Further reading

What is the `useReducer` hook in React and when should it be used?

Topics
React

TL;DR

The useReducer hook in React is used for managing complex state logic in functional components. It is an alternative to useState and is particularly useful when the state has multiple sub-values or when the next state depends on the previous one. It takes a reducer function and an initial state as arguments and returns the current state and a dispatch function.

const [state, dispatch] = useReducer(reducer, initialState);

Use useReducer when you have complex state logic that involves multiple sub-values or when the next state depends on the previous state.


What is the useReducer hook in React and when should it be used?

Introduction to useReducer

The useReducer hook is a React hook that is used for managing state in functional components. It is an alternative to the useState hook and is particularly useful for managing more complex state logic. The useReducer hook is similar to the reduce function in JavaScript arrays, where you have a reducer function that determines how the state should change in response to actions.

Syntax

The useReducer hook takes two arguments: a reducer function and an initial state. It returns an array with the current state and a dispatch function.

const [state, dispatch] = useReducer(reducer, initialState);

Reducer function

The reducer function is a pure function that takes the current state and an action as arguments and returns the new state. The action is an object that typically has a type property and an optional payload.

function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}

Example usage

Here is a simple example of using useReducer to manage a counter state:

import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
export default Counter;

When to use useReducer

  • Complex state logic: Use useReducer when you have complex state logic that involves multiple sub-values or when the next state depends on the previous state.
  • State management: It is useful when you need a more predictable state management pattern, similar to Redux.
  • Performance optimization: useReducer can help optimize performance in certain scenarios by avoiding unnecessary re-renders.

Further reading

What is the `useId` hook in React and when should it be used?

Topics
React

TL;DR

The useId hook in React is used to generate unique IDs for elements within a component. This is particularly useful for accessibility purposes, such as linking form inputs with their labels. It ensures that IDs are unique across the entire application, even if the component is rendered multiple times.

import { useId } from 'react';
function MyComponent() {
const id = useId();
return (
<div>
<label htmlFor={id}>Name:</label>
<input id={id} type="text" />
</div>
);
}

What is the useId hook in React and when should it be used?

Introduction to useId

The useId hook is a built-in React hook introduced in React 18. It is designed to generate unique IDs that can be used within a component. This is particularly useful for ensuring that IDs are unique across the entire application, even if the component is rendered multiple times.

When to use useId

Accessibility

One of the primary use cases for useId is to improve accessibility. For example, when creating form elements, it is important to link labels to their corresponding inputs using the htmlFor attribute on the label and the id attribute on the input. The useId hook ensures that these IDs are unique, preventing any potential conflicts.

import { useId } from 'react';
function MyComponent() {
const id = useId();
return (
<div>
<label htmlFor={id}>Name:</label>
<input id={id} type="text" />
</div>
);
}
Dynamic components

When you have components that are rendered dynamically or multiple times, using useId ensures that each instance of the component has a unique ID. This can prevent issues where multiple elements end up with the same ID, which can cause problems with accessibility and JavaScript behavior.

import { useId } from 'react';
function DynamicComponent() {
const id = useId();
return (
<div>
<label htmlFor={id}>Dynamic Input:</label>
<input id={id} type="text" />
</div>
);
}
function App() {
return (
<div>
<DynamicComponent />
<DynamicComponent />
</div>
);
}

How useId works

The useId hook generates a unique string that can be used as an ID. This string is unique across the entire application, ensuring that there are no conflicts even if the component is rendered multiple times. The hook does not take any arguments and returns a string.

import { useId } from 'react';
function ExampleComponent() {
const id = useId();
console.log(id); // Outputs a unique ID string
return <div id={id}>Unique ID Component</div>;
}

Best practices

  • Use for accessibility: Always use useId when linking labels to inputs to ensure accessibility.
  • Avoid hardcoding IDs: Instead of hardcoding IDs, use useId to generate them dynamically.
  • Consistent usage: Use useId consistently across your application to avoid ID conflicts.

Further reading

What are React Fragments used for?

Topics
React

TL;DR

React Fragments are used to group multiple elements without adding extra nodes to the DOM. This is useful when you want to return multiple elements from a component's render method without wrapping them in an additional HTML element. You can use the shorthand syntax <>...</> or the React.Fragment syntax.

return (
<>
<ChildComponent1 />
<ChildComponent2 />
</>
);

What are React Fragments used for?

Grouping multiple elements

React Fragments allow you to group multiple elements without adding extra nodes to the DOM. This is particularly useful when you want to return multiple elements from a component's render method but don't want to introduce unnecessary wrapper elements.

Avoiding unnecessary DOM nodes

Using React Fragments helps in avoiding unnecessary DOM nodes, which can be beneficial for performance and maintaining a cleaner DOM structure. This is especially important in complex applications where additional nodes can lead to increased memory usage and slower rendering times.

Syntax

There are two ways to use React Fragments:

  1. Shorthand syntax: This is the most concise way to use fragments. It uses empty tags <>...</>.

    return (
    <>
    <ChildComponent1 />
    <ChildComponent2 />
    </>
    );
  2. Full syntax: This uses React.Fragment and can be useful if you need to add keys to the fragment.

    return (
    <React.Fragment>
    <ChildComponent1 />
    <ChildComponent2 />
    </React.Fragment>
    );

Adding keys to fragments

If you need to add keys to the elements within a fragment, you must use the full React.Fragment syntax. This is useful when rendering a list of elements.

return (
<>
{items.map((item) => (
<React.Fragment key={item.id}>
<ChildComponent />
</React.Fragment>
))}
</>
);

Use cases

  • Returning multiple elements from a component: When a component needs to return multiple sibling elements, using a fragment can help avoid unnecessary wrapper elements.
  • Rendering lists: When rendering a list of elements, fragments can be used to group the list items without adding extra nodes to the DOM.
  • Conditional rendering: When conditionally rendering multiple elements, fragments can help keep the DOM structure clean.

Further reading

What is `forwardRef()` in React used for?

Topics
React

TL;DR

forwardRef() in React is used to pass a ref through a component to one of its child components. This is useful when you need to access a DOM element or a child component's instance directly from a parent component. You wrap your functional component with forwardRef() and use the ref parameter to forward the ref to the desired child element.

import React, { forwardRef } from 'react';
const MyComponent = forwardRef((props, ref) => <input ref={ref} {...props} />);

What is forwardRef() in React used for?

Introduction

In React, forwardRef() is a higher-order function that allows you to forward a ref through a component to one of its child components. This is particularly useful when you need to access a DOM element or a child component's instance directly from a parent component.

Why use forwardRef()?

There are several scenarios where forwardRef() is beneficial:

  • Accessing DOM elements: When you need to manipulate a DOM element directly, such as focusing an input field.
  • Interacting with child components: When you need to call methods or access properties of a child component instance.

How to use forwardRef()

To use forwardRef(), you wrap your functional component with it and use the ref parameter to forward the ref to the desired child element.

Example

Here is a simple example demonstrating how to use forwardRef():

import React, { forwardRef, useRef } from 'react';
// Define a functional component and wrap it with forwardRef
const MyInput = forwardRef((props, ref) => <input ref={ref} {...props} />);
const ParentComponent = () => {
const inputRef = useRef(null);
const focusInput = () => {
// Access the input element and focus it
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<div>
<MyInput ref={inputRef} placeholder="Type here..." />
<button onClick={focusInput}>Focus Input</button>
</div>
);
};
export default ParentComponent;

In this example:

  1. MyInput is a functional component wrapped with forwardRef().
  2. The ref parameter is forwarded to the input element inside MyInput.
  3. In ParentComponent, a ref (inputRef) is created using useRef().
  4. The inputRef is passed to MyInput, allowing the parent component to access the input element directly.
  5. The focusInput function uses the ref to focus the input element when the button is clicked.

Important considerations

  • Functional components only: forwardRef() is used with functional components. Class components can directly use refs without forwardRef().
  • Ref forwarding: Ensure that the ref is forwarded to a DOM element or a class component instance, not another functional component.

Further reading

How do you reset a component's state in React?

Topics
React

TL;DR

To reset a component's state in React, you can set the state back to its initial value. This can be done by defining an initial state and then using the setState function to reset it. For example, if you have a state object like this:

const [state, setState] = useState(initialState);

You can reset it by calling:

setState(initialState);

How do you reset a component's state in React?

Using functional components with hooks

In functional components, you can use the useState hook to manage state. To reset the state, you can simply call the setState function with the initial state value.

Example
import React, { useState } from 'react';
const MyComponent = () => {
const initialState = { count: 0, text: '' };
const [state, setState] = useState(initialState);
const resetState = () => {
setState(initialState);
};
return (
<div>
<p>Count: {state.count}</p>
<p>Text: {state.text}</p>
<button onClick={resetState}>Reset</button>
</div>
);
};
export default MyComponent;

Using class components

In class components, you can reset the state by calling this.setState with the initial state value.

Example
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.initialState = { count: 0, text: '' };
this.state = this.initialState;
}
resetState = () => {
this.setState(this.initialState);
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<p>Text: {this.state.text}</p>
<button onClick={this.resetState}>Reset</button>
</div>
);
}
}
export default MyComponent;

Using a function to generate initial state

Sometimes, the initial state might be derived from props or other dynamic sources. In such cases, you can use a function to generate the initial state.

Example
import React, { useState } from 'react';
const MyComponent = (props) => {
const getInitialState = () => ({ count: props.initialCount, text: '' });
const [state, setState] = useState(getInitialState);
const resetState = () => {
setState(getInitialState());
};
return (
<div>
<p>Count: {state.count}</p>
<p>Text: {state.text}</p>
<button onClick={resetState}>Reset</button>
</div>
);
};
export default MyComponent;

Further reading

What are error boundaries in React for?

Topics
React

TL;DR

Error boundaries in React are components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of crashing the whole application. They are implemented using the componentDidCatch lifecycle method and the static getDerivedStateFromError method. Error boundaries do not catch errors inside event handlers, asynchronous code, or server-side rendering.


What are error boundaries in React for?

Introduction

Error boundaries are a feature in React that help manage and handle errors in a more graceful way. They allow developers to catch JavaScript errors anywhere in their component tree, log those errors, and display a fallback UI instead of crashing the entire application.

How to implement error boundaries

To create an error boundary, you need to define a class component that implements either or both of the following lifecycle methods:

  • static getDerivedStateFromError(error): This method is used to update the state so the next render will show the fallback UI.
  • componentDidCatch(error, info): This method is used to log error information.

Here is an example of an error boundary component:

import React, { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render shows the fallback UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error('Error caught by ErrorBoundary: ', error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;

Usage

To use the error boundary, wrap it around any component that you want to monitor for errors:

<ErrorBoundary>
<MyComponent />
</ErrorBoundary>

Limitations

Error boundaries have some limitations:

  • They do not catch errors inside event handlers. For event handlers, you need to use regular JavaScript try/catch blocks.
  • They do not catch errors in asynchronous code (e.g., setTimeout or requestAnimationFrame callbacks).
  • They do not catch errors during server-side rendering.
  • They do not catch errors thrown in the error boundary itself.

Best practices

  • Use error boundaries to wrap high-level components such as route handlers or major sections of your application.
  • Log errors to an error reporting service to keep track of issues in production.
  • Provide a user-friendly fallback UI to improve the user experience when an error occurs.

Further reading

How do you test React applications?

Topics
React

TL;DR

To test React applications, you can use tools like Jest and React Testing Library. Jest is a JavaScript testing framework that works well with React, and React Testing Library provides utilities to test React components in a way that resembles how users interact with them. You can write unit tests for individual components, integration tests for component interactions, and end-to-end tests using tools like Cypress.


How do you test React applications?

Unit testing

Unit testing involves testing individual components in isolation. Jest is a popular choice for unit testing React applications. It provides a test runner, assertion library, and mocking capabilities.

Example
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import MyComponent from './MyComponent';
test('renders the component with the correct text', () => {
render(<MyComponent />);
expect(screen.getByText('Hello, World!')).toBeInTheDocument();
});

Integration testing

Integration testing involves testing the interaction between multiple components. React Testing Library is useful for this as it allows you to render components and interact with them as a user would.

Example
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import ParentComponent from './ParentComponent';
test('updates child component when parent state changes', () => {
render(<ParentComponent />);
fireEvent.click(screen.getByText('Update Child'));
expect(screen.getByText('Child Updated')).toBeInTheDocument();
});

End-to-end testing

End-to-end (E2E) testing involves testing the entire application flow from the user's perspective. Cypress is a popular tool for E2E testing in React applications.

Example
describe('My Application', () => {
it('should allow a user to log in', () => {
cy.visit('/login');
cy.get('input[name="username"]').type('user');
cy.get('input[name="password"]').type('password');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
});
});

Snapshot testing

Snapshot testing involves capturing the rendered output of a component and comparing it to a saved snapshot. Jest provides built-in support for snapshot testing.

Example
import React from 'react';
import renderer from 'react-test-renderer';
import MyComponent from './MyComponent';
test('matches the snapshot', () => {
const tree = renderer.create(<MyComponent />).toJSON();
expect(tree).toMatchSnapshot();
});

Further reading

What is React strict mode and what are its benefits?

Topics
React

TL;DR

React strict mode is a development tool that helps identify potential problems in an application. It activates additional checks and warnings for its descendants. It doesn't render any visible UI and doesn't affect the production build. The benefits include identifying unsafe lifecycle methods, warning about legacy string ref API usage, detecting unexpected side effects, and ensuring that components are resilient to future changes.


What is React strict mode and what are its benefits?

What is React strict mode?

React strict mode is a feature in React that helps developers identify potential problems in their applications. It is a wrapper component that you can use to wrap parts of your application to enable additional checks and warnings. It does not render any visible UI and does not affect the production build.

To use React strict mode, you can wrap your component tree with the StrictMode component:

import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);

Benefits of React strict mode

Identifying unsafe lifecycle methods

React strict mode helps identify components that use unsafe lifecycle methods, such as componentWillMount, componentWillReceiveProps, and componentWillUpdate. These methods are considered unsafe because they can lead to bugs and unexpected behavior. React strict mode will warn you if any of your components use these methods.

Warning about legacy string ref API usage

React strict mode warns you if you are using the legacy string ref API. The string ref API is considered legacy and is not recommended for use in new code. Instead, you should use the callback ref API or the React.createRef API.

Detecting unexpected side effects

React strict mode helps detect unexpected side effects by intentionally double-invoking certain lifecycle methods and functions. This helps ensure that your components are resilient to future changes and that they do not rely on side effects that may not always be executed.

Ensuring components are resilient to future changes

By enabling React strict mode, you can ensure that your components are more resilient to future changes in React. The additional checks and warnings help you identify potential issues early, making it easier to maintain and update your codebase.

Further reading

How do you localize React applications?

Topics
ReactInternationalization

TL;DR

To localize a React application, you typically use a library like react-i18next or react-intl. First, you set up your translation files for different languages. Then, you configure the localization library in your React app. Finally, you use the provided hooks or components to display localized text in your components.

// Example using react-i18next
import { useTranslation } from 'react-i18next';
const MyComponent = () => {
const { t } = useTranslation();
return <p>{t('welcome_message')}</p>;
};

Setting up localization in React

Choosing a localization library

There are several libraries available for localizing React applications, with react-i18next and react-intl being among the most popular. For this guide, we'll focus on react-i18next.

Installing the library

First, install the necessary packages:

npm install i18next react-i18next

Setting up translation files

Create JSON files for each language you want to support. For example, create en.json and fr.json in a locales directory:

// locales/en.json
{
"welcome_message": "Welcome to our application!"
}
// locales/fr.json
{
"welcome_message": "Bienvenue dans notre application!"
}

Configuring the localization library

Set up i18next and react-i18next in your application. Create an i18n.js file for the configuration:

// i18n.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import en from './locales/en.json';
import fr from './locales/fr.json';
i18n.use(initReactI18next).init({
resources: {
en: { translation: en },
fr: { translation: fr },
},
lng: 'en', // default language
fallbackLng: 'en',
interpolation: {
escapeValue: false, // react already safes from xss
},
});
export default i18n;

Integrating with your React application

Wrap your application with the I18nextProvider and import the i18n configuration:

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
ReactDOM.render(
<I18nextProvider i18n={i18n}>
<App />
</I18nextProvider>,
document.getElementById('root'),
);

Using translations in components

Use the useTranslation hook to access the t function for translating text:

// MyComponent.js
import React from 'react';
import { useTranslation } from 'react-i18next';
const MyComponent = () => {
const { t } = useTranslation();
return <p>{t('welcome_message')}</p>;
};
export default MyComponent;

Switching languages

To switch languages, use the i18n.changeLanguage method:

// LanguageSwitcher.js
import React from 'react';
import { useTranslation } from 'react-i18next';
const LanguageSwitcher = () => {
const { i18n } = useTranslation();
const changeLanguage = (lng) => {
i18n.changeLanguage(lng);
};
return (
<div>
<button onClick={() => changeLanguage('en')}>English</button>
<button onClick={() => changeLanguage('fr')}>Français</button>
</div>
);
};
export default LanguageSwitcher;

Further reading

What is code splitting in a React application?

Topics
React

TL;DR

Code splitting in a React application is a technique used to improve performance by splitting the code into smaller chunks that can be loaded on demand. This helps in reducing the initial load time of the application. You can achieve code splitting using dynamic import() statements or React's React.lazy and Suspense.

// Using React.lazy and Suspense
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>
);
}

What is code splitting in a React application?

Introduction

Code splitting is a performance optimization technique that involves breaking down your application's code into smaller, more manageable chunks. This allows the application to load only the necessary code initially and defer the loading of other parts until they are needed. This can significantly reduce the initial load time and improve the overall user experience.

How to implement code splitting

Using dynamic import()

Dynamic import() is a JavaScript feature that allows you to load modules asynchronously. This can be used to split your code into separate chunks.

// Dynamic import example
import('./module').then((module) => {
// Use the module
});
Using React.lazy and Suspense

React provides built-in support for code splitting through React.lazy and Suspense. React.lazy allows you to render a dynamic import as a regular component, and Suspense lets you specify a loading fallback while the component is being loaded.

// Lazy loading a component
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>
);
}

Benefits of code splitting

  • Improved performance: By loading only the necessary code initially, you can reduce the initial load time of your application.
  • Better user experience: Faster load times lead to a smoother and more responsive user experience.
  • Efficient resource usage: Code splitting ensures that resources are used more efficiently by loading code only when it is needed.

Tools and libraries

  • Webpack: Webpack is a popular module bundler that supports code splitting out of the box. You can configure it to split your code into chunks automatically.
  • React Loadable: Although React.lazy and Suspense are the recommended ways to implement code splitting in React, React Loadable is an older library that also provides similar functionality.

Further reading

How would one optimize the performance of React contexts to reduce rerenders?

Topics
ReactPerformance

TL;DR

To optimize the performance of React contexts and reduce rerenders, you can use techniques such as memoizing context values, splitting contexts, and using selectors. Memoizing context values with useMemo ensures that the context value only changes when its dependencies change. Splitting contexts allows you to isolate state changes to specific parts of your application. Using selectors with libraries like use-context-selector can help you only rerender components that actually need the updated context value.

const value = useMemo(() => ({ state, dispatch }), [state, dispatch]);

How to optimize the performance of React contexts to reduce rerenders

Memoizing context values

One of the most effective ways to reduce unnecessary rerenders is to memoize the context value. By using useMemo, you can ensure that the context value only changes when its dependencies change.

import React, { createContext, useMemo, useState } from 'react';
const MyContext = createContext();
const MyProvider = ({ children }) => {
const [state, setState] = useState(initialState);
const value = useMemo(() => ({ state, setState }), [state]);
return <MyContext.Provider value={value}>{children}</MyContext.Provider>;
};

Splitting contexts

Another technique is to split your context into multiple smaller contexts. This way, you can isolate state changes to specific parts of your application, reducing the number of components that need to rerender.

const UserContext = createContext();
const ThemeContext = createContext();
const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};

Using selectors

Using selectors can help you only rerender components that actually need the updated context value. Libraries like use-context-selector can be very useful for this purpose.

import { createContext, useContextSelector } from 'use-context-selector';
const MyContext = createContext();
const MyComponent = () => {
const state = useContextSelector(MyContext, (v) => v.state);
return <div>{state}</div>;
};

Further reading

What are higher order components in React?

Topics
React

TL;DR

Higher order components (HOCs) in React are functions that take a component and return a new component with additional props or behavior. They are used to reuse component logic. For example, if you have a component MyComponent, you can create an HOC like this:

const withExtraProps = (WrappedComponent) => {
return (props) => <WrappedComponent {...props} extraProp="value" />;
};
const EnhancedComponent = withExtraProps(MyComponent);

What are higher order components in React?

Definition

Higher order components (HOCs) are functions in React that take a component as an argument and return a new component. The new component typically wraps the original component and adds additional props, state, or behavior. HOCs are a pattern for reusing component logic.

Purpose

HOCs are used to:

  • Share common functionality between components
  • Abstract and reuse component logic
  • Enhance components with additional props or state

Example

Here is a simple example of an HOC that adds an extraProp to a wrapped component:

import React from 'react';
// Define the HOC
const withExtraProps = (WrappedComponent) => {
return (props) => {
return <WrappedComponent {...props} extraProp="value" />;
};
};
// Define a component to be wrapped
const MyComponent = (props) => {
return <div>{props.extraProp}</div>;
};
// Wrap the component using the HOC
const EnhancedComponent = withExtraProps(MyComponent);
// Use the enhanced component
const App = () => {
return <EnhancedComponent />;
};
export default App;

In this example, withExtraProps is an HOC that adds an extraProp to MyComponent. The EnhancedComponent now has access to extraProp.

Common use cases

  • Authentication: Wrapping components to check if a user is authenticated before rendering.
  • Logging: Adding logging functionality to components.
  • Theming: Injecting theme-related props into components.
  • Data fetching: Fetching data and passing it as props to components.

Best practices

  • Do not mutate the original component: Always return a new component.
  • Use HOCs sparingly: Overusing HOCs can make the code harder to understand.
  • Name the HOC properly: Use a descriptive name that indicates what the HOC does.

Alternatives

  • Render props: A pattern where a component uses a function as a prop to determine what to render.
  • Hooks: Custom hooks can be used to share logic between functional components.

Further reading

What is the Flux pattern and what are its benefits?

Topics
React

TL;DR

The Flux pattern is an architectural design used for managing state in applications, particularly in React ecosystems. It enforces a unidirectional data flow, making it easier to manage and debug application state.

  • Core components:
    • Dispatcher: Manages actions and dispatches them to stores.
    • Stores: Hold the state and logic of the application.
    • Actions: Payloads of information sent from the application to the dispatcher.
    • View: React components that re-render when stores update.
  • Benefits:
    • Predictable state management due to unidirectional data flow.
    • Improved debugging and testing.
    • Clear separation of concerns.

Example flow:

  1. User interacts with the View.
  2. Actions are triggered and dispatched by the Dispatcher.
  3. Stores process the actions and update their state.
  4. View re-renders based on the updated state.

What is the Flux pattern?

Overview

Flux is a design pattern introduced by Facebook to manage the flow of data in React applications. It enforces a unidirectional data flow, where data flows in one direction through specific components:

  1. Dispatcher: Acts as a central hub, dispatching actions to stores.
  2. Stores: Manage the application's state and contain the business logic.
  3. Actions: Represent the payloads of information sent to the dispatcher.
  4. View: Consists of React components that listen to stores for changes and re-render accordingly.

This structure simplifies state management, especially for complex applications, by ensuring data flows in a predictable and traceable manner.

Unidirectional data flow

Unlike traditional MVC patterns, where data can flow in multiple directions, Flux's unidirectional flow ensures consistency:

  1. User interactions trigger actions.
  2. Actions are sent to the dispatcher, which forwards them to stores.
  3. Stores update their state and notify the view to re-render.

Code example

const Dispatcher = require('flux').Dispatcher;
const dispatcher = new Dispatcher();
// Action
const action = {
type: 'INCREMENT',
payload: { amount: 1 },
};
dispatcher.dispatch(action);
// Store
class CounterStore {
constructor() {
this.count = 0;
dispatcher.register((action) => {
if (action.type === 'INCREMENT') {
this.count += action.payload.amount;
console.log(`Count: ${this.count}`);
}
});
}
}
const store = new CounterStore();
// Dispatching an action
dispatcher.dispatch({ type: 'INCREMENT', payload: { amount: 1 } });

Benefits of the Flux pattern

Predictable state management

The unidirectional data flow ensures that the application's state transitions are clear and predictable, making it easier to understand and debug.

Improved debugging and testing

  • Each action represents a discrete event, making it easier to trace changes in the application.
  • Stores contain pure logic, which can be unit tested independently of the view.

Scalability

  • As the application grows, the Flux pattern helps maintain a clear structure.
  • Decoupled components allow for modular development.

Clear separation of concerns

  • Actions encapsulate events and payloads.
  • Stores handle state and business logic.
  • Views focus on rendering the UI.

Compatibility with React

Flux's unidirectional data flow aligns well with React's declarative component model, enabling seamless integration.

Further reading

Explain one-way data flow of React and its benefits

Topics
React

TL;DR

In React, one-way data flow means that data in a React application flows in a single direction, from parent components to child components. This makes the data flow predictable and easier to debug. The main benefits include improved maintainability, easier debugging, and better performance.


One-way data flow of React and its benefits

What is one-way data flow?

In React, one-way data flow refers to the concept where data flows in a single direction, from parent components to child components. This is achieved through the use of props. Parent components pass data to child components via props, and child components can only read these props but cannot modify them. If a child component needs to communicate back to the parent, it does so by calling a function passed down from the parent as a prop.

Example

Here is a simple example to illustrate one-way data flow:

// ParentComponent.jsx
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
const [data, setData] = useState('Hello from Parent');
const handleChange = (newData) => {
setData(newData);
};
return (
<div>
<h1>{data}</h1>
<ChildComponent data={data} onChange={handleChange} />
</div>
);
};
export default ParentComponent;
// ChildComponent.jsx
import React from 'react';
const ChildComponent = ({ data, onChange }) => {
return (
<div>
<p>{data}</p>
<button onClick={() => onChange('Hello from Child')}>Change Data</button>
</div>
);
};
export default ChildComponent;

In this example, ParentComponent passes data and handleChange function to ChildComponent via props. The ChildComponent can read the data and call onChange to communicate back to the parent.

Benefits of one-way data flow

Improved maintainability

One-way data flow makes the application structure more predictable and easier to understand. Since data flows in a single direction, it is easier to track how data changes over time, making the codebase more maintainable.

Easier debugging

With one-way data flow, it is easier to pinpoint where a bug might be occurring. Since data can only flow from parent to child, you can trace the data flow and identify the source of the issue more quickly.

Better performance

One-way data flow can lead to better performance because it reduces the complexity of data management. React's reconciliation algorithm can efficiently update the DOM by comparing the current state with the previous state, minimizing the number of updates required.

Further reading

How do you handle asynchronous data loading in React applications?

Topics
ReactAsync

TL;DR

In React applications, asynchronous data loading is typically handled using useEffect and useState hooks. You initiate the data fetch inside useEffect and update the state with the fetched data. This ensures that the component re-renders with the new data. Here's a simple example:

import React, { useState, useEffect } from 'react';
function DataFetchingComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
setLoading(false);
}
fetchData();
}, []);
if (loading) {
return <div>Loading...</div>;
}
return <div>{JSON.stringify(data)}</div>;
}

Handling asynchronous data loading in React applications

Using useEffect and useState

The most common way to handle asynchronous data loading in React is by using the useEffect and useState hooks. useEffect allows you to perform side effects, such as data fetching, and useState helps manage the component's state.

  1. Initialize state: Use useState to create state variables for storing the fetched data and loading status.
  2. Fetch data: Use useEffect to perform the data fetching when the component mounts.
  3. Update state: Once the data is fetched, update the state to trigger a re-render.

Here's a detailed example:

import React, { useState, useEffect } from 'react';
function DataFetchingComponent() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Error fetching data:', error);
} finally {
setLoading(false);
}
}
fetchData();
}, []);
if (loading) {
return <div>Loading...</div>;
}
return <div>{JSON.stringify(data)}</div>;
}

Handling errors

It's important to handle errors that may occur during data fetching. You can use a try-catch block within the useEffect to catch and handle errors.

useEffect(() => {
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Error fetching data:', error);
} finally {
setLoading(false);
}
}
fetchData();
}, []);

Using custom hooks

For better code reusability, you can create custom hooks to handle data fetching. This allows you to encapsulate the data fetching logic and reuse it across multiple components.

import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;

You can then use this custom hook in your components:

import React from 'react';
import useFetch from './useFetch';
function DataFetchingComponent() {
const { data, loading, error } = useFetch('https://api.example.com/data');
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
return <div>{JSON.stringify(data)}</div>;
}

Further reading

Explain server-side rendering of React applications and its benefits?

Topics
React

TL;DR

Server-side rendering (SSR) in React involves rendering React components on the server and sending the fully rendered HTML to the client. This approach improves initial load times and SEO. The server handles the initial rendering, and the client takes over with React's hydration process. Benefits include faster initial page loads, better SEO, and improved performance on slower devices.


What is server-side rendering of React applications?

Definition

Server-side rendering (SSR) is a technique where the server renders the initial HTML of a React application and sends it to the client. This is in contrast to client-side rendering (CSR), where the browser downloads a minimal HTML page and renders the content using JavaScript.

How it works

  1. Initial request: When a user requests a page, the server processes this request.
  2. Rendering on the server: The server uses React to render the components into HTML.
  3. Sending HTML to the client: The server sends the fully rendered HTML to the client.
  4. Hydration: Once the HTML is loaded, React takes over and binds the event handlers, making the page interactive.

Code example

Here's a basic example using Next.js, a popular React framework that supports SSR out of the box:

import React from 'react';
const Home = ({ data }) => (
<div>
<h1>Welcome to my SSR React App</h1>
<p>Data from server: {data}</p>
</div>
);
export async function getServerSideProps() {
// Fetch data from an API or database
const data = await fetchDataFromAPI();
return { props: { data } };
}
export default Home;

Benefits of server-side rendering

Improved initial load time

  • Faster content display: Since the server sends fully rendered HTML, users see the content faster compared to CSR, where the browser has to download and execute JavaScript before rendering.

Better SEO

  • Search engine indexing: Search engines can easily index the fully rendered HTML, improving the SEO of your application. This is particularly important for content-heavy sites.

Performance on slower devices

  • Reduced client-side processing: SSR reduces the amount of JavaScript that needs to be processed on the client side, which is beneficial for users with slower devices or poor network conditions.

Enhanced user experience

  • Perceived performance: Users perceive the application as faster because they see content sooner, even if the JavaScript is still loading in the background.

Further reading

Explain static generation of React applications and its benefits?

Topics
React

TL;DR

Static generation in React applications involves pre-rendering the HTML at build time, rather than on each request. This results in faster load times and better performance since the HTML is already generated and can be served directly from a CDN. It also improves SEO and can reduce server load. Tools like Next.js facilitate static generation by allowing developers to generate static pages easily.


Static generation of React applications and its benefits

What is static generation?

Static generation is a method of pre-rendering where the HTML of a page is generated at build time. This means that the HTML is created once, during the build process, and then reused for each request. In the context of React applications, this is often achieved using frameworks like Next.js.

How does static generation work?

  1. Build time rendering: During the build process, the framework generates the HTML for each page based on the React components and data.
  2. Static files: The generated HTML, CSS, and JavaScript files are then stored as static files.
  3. Serving the files: These static files can be served directly from a CDN or a web server, without the need for server-side rendering on each request.

Benefits of static generation

Improved performance
  • Faster load times: Since the HTML is pre-generated, it can be served immediately without waiting for server-side rendering.
  • Reduced server load: Static files can be served from a CDN, reducing the load on the origin server.
Better SEO
  • Search engine indexing: Pre-rendered HTML is more easily indexed by search engines, improving SEO.
  • Consistent content: The content is consistent across requests, ensuring that search engines see the same content as users.
Enhanced security
  • No server-side code execution: Since there is no server-side rendering, there is less risk of server-side vulnerabilities.
  • Static files: Serving static files reduces the attack surface compared to dynamic content generation.
Scalability
  • CDN distribution: Static files can be distributed across multiple CDN nodes, improving scalability and reducing latency.
  • Efficient caching: Static files can be easily cached, further improving performance and reducing server load.

Example with Next.js

Next.js is a popular framework for React that supports static generation. Here is a simple example of how to use static generation in Next.js:

// pages/index.js
import React from 'react';
const HomePage = ({ data }) => {
return (
<div>
<h1>Welcome to my static site!</h1>
<p>{data.message}</p>
</div>
);
};
export async function getStaticProps() {
// Fetch data at build time
const data = { message: 'Hello, world!' };
return {
props: {
data,
},
};
}
export default HomePage;

In this example, the getStaticProps function fetches data at build time, and the HomePage component uses this data to render the HTML.

Further reading

Explain the presentational vs container component pattern in React

Topics
React

TL;DR

In React, the presentational vs container component pattern is a design approach where presentational components focus on how things look and container components focus on how things work. Presentational components are concerned with rendering HTML and CSS, while container components handle the logic and state management. This separation helps in maintaining a clean and organized codebase.


Presentational vs container component pattern in React

Presentational components

Presentational components are primarily concerned with the UI. They receive data and callbacks exclusively via props and rarely have their own state (except for UI state like hover or active). They are often stateless functional components but can also be class components.

Characteristics
  • Focus on how things look
  • Receive data and callbacks via props
  • Rarely have their own state
  • Typically written as functional components
  • Do not use Redux or other state management libraries directly
Example
const Button = ({ onClick, label }) => (
<button onClick={onClick}>{label}</button>
);

Container components

Container components are concerned with how things work. They manage state and handle business logic. They often fetch data, handle user interactions, and pass data down to presentational components as props.

Characteristics
  • Focus on how things work
  • Manage state and handle business logic
  • Fetch data and handle user interactions
  • Pass data and callbacks to presentational components
  • Often use Redux or other state management libraries
Example
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchData } from './actions';
import Button from './Button';
class ButtonContainer extends Component {
componentDidMount() {
this.props.fetchData();
}
handleClick = () => {
// Handle button click
};
render() {
return <Button onClick={this.handleClick} label="Click me" />;
}
}
const mapDispatchToProps = {
fetchData,
};
export default connect(null, mapDispatchToProps)(ButtonContainer);

Benefits

  • Separation of concerns: By separating the UI from the logic, the codebase becomes more modular and easier to maintain.
  • Reusability: Presentational components can be reused across different parts of the application since they are not tied to specific logic.
  • Testability: Presentational components are easier to test because they are stateless and only rely on props.

Further reading

What are some common pitfalls when doing data fetching in React?

Topics
React

TL;DR

Common pitfalls when doing data fetching in React include not handling loading and error states, causing memory leaks by not cleaning up subscriptions, and not using the right lifecycle methods or hooks. Always ensure you handle these states properly, clean up after your components, and use useEffect for side effects in functional components.


Common pitfalls when doing data fetching in React

Not handling loading and error states

When fetching data, it's crucial to manage the different states of the request: loading, success, and error. Failing to do so can lead to a poor user experience.

const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => {
setData(data);
setLoading(false);
})
.catch((error) => {
setError(error);
setLoading(false);
});
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{JSON.stringify(data)}</div>;

Causing memory leaks by not cleaning up subscriptions

When a component unmounts before a fetch request completes, it can cause memory leaks. To prevent this, you should clean up any ongoing requests.

useEffect(() => {
let isMounted = true;
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => {
if (isMounted) {
setData(data);
setLoading(false);
}
})
.catch((error) => {
if (isMounted) {
setError(error);
setLoading(false);
}
});
return () => {
isMounted = false;
};
}, []);

Not using the right lifecycle methods or hooks

In class components, data fetching should be done in componentDidMount. In functional components, use the useEffect hook.

// Class component
class MyComponent extends React.Component {
componentDidMount() {
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => this.setState({ data, loading: false }))
.catch((error) => this.setState({ error, loading: false }));
}
}
// Functional component
const MyComponent = () => {
useEffect(() => {
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => setError(error));
}, []);
};

Ignoring dependency arrays in useEffect

The dependency array in useEffect determines when the effect runs. Ignoring it can lead to unnecessary re-renders or missed updates.

useEffect(() => {
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => setError(error));
}, []); // Empty array means this effect runs once after the initial render

Fetching data in the render method

Fetching data directly in the render method can cause infinite loops and performance issues. Always use lifecycle methods or hooks.

// Incorrect
const MyComponent = () => {
const data = fetch('https://api.example.com/data').then((response) =>
response.json(),
);
return <div>{JSON.stringify(data)}</div>;
};
// Correct
const MyComponent = () => {
useEffect(() => {
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => setError(error));
}, []);
};

Further reading

What are render props in React and what are they for?

Topics
React

TL;DR

Render props in React are a technique for sharing code between components using a prop whose value is a function. This function returns a React element and allows you to pass data to the child component. It helps in reusing component logic without using higher-order components or hooks.

class DataFetcher extends React.Component {
state = { data: null };
componentDidMount() {
fetch(this.props.url)
.then((response) => response.json())
.then((data) => this.setState({ data }));
}
render() {
return this.props.render(this.state.data);
}
}
// Usage
<DataFetcher
url="/api/data"
render={(data) => <div>{data ? data.name : 'Loading...'}</div>}
/>;

What are render props in React and what are they for?

Definition

Render props is a pattern in React for sharing code between components using a prop whose value is a function. This function is called a "render prop" because it is used to determine what to render.

Purpose

Render props are used to:

  • Share logic between components without using higher-order components (HOCs) or hooks
  • Make components more reusable and composable
  • Improve code readability and maintainability

How it works

A component that uses a render prop takes a function as a prop. This function is called within the component's render method to produce the desired output.

Example

Here is a simple example to illustrate the concept:

class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY,
});
};
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
// Usage
<MouseTracker
render={({ x, y }) => (
<h1>
The mouse position is ({x}, {y})
</h1>
)}
/>;

In this example, MouseTracker is a component that tracks the mouse position and passes the coordinates to the render prop function. The render prop function then determines how to display the coordinates.

Benefits

  • Reusability: The logic for tracking the mouse position is encapsulated in MouseTracker, making it reusable across different parts of the application.
  • Separation of concerns: The MouseTracker component is responsible for tracking the mouse position, while the render prop function is responsible for rendering the UI.
  • Flexibility: Different UI representations can be created by passing different render prop functions to the same MouseTracker component.

Further reading

What are some React anti-patterns?

Topics
React

TL;DR

React anti-patterns are practices that can lead to inefficient, hard-to-maintain, or buggy code. Some common anti-patterns include:

  • Mutating state directly instead of using setState
  • Using componentWillMount for data fetching
  • Overusing componentWillReceiveProps
  • Not using keys in lists
  • Overusing inline functions in render
  • Deeply nested state

Common React anti-patterns

Mutating state directly

Directly mutating the state can lead to unexpected behavior and bugs. Always use setState to update the state.

// Anti-pattern
this.state.count = 1;
// Correct approach
this.setState({ count: 1 });

Using componentWillMount for data fetching

componentWillMount is deprecated and should not be used for data fetching. Use componentDidMount instead.

// Anti-pattern
componentWillMount() {
fetchData();
}
// Correct approach
componentDidMount() {
fetchData();
}

Overusing componentWillReceiveProps

componentWillReceiveProps is deprecated. Use getDerivedStateFromProps or componentDidUpdate instead.

// Anti-pattern
componentWillReceiveProps(nextProps) {
if (nextProps.value !== this.props.value) {
this.setState({ value: nextProps.value });
}
}
// Correct approach
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.value !== prevState.value) {
return { value: nextProps.value };
}
return null;
}

Not using keys in lists

Keys help React identify which items have changed, are added, or are removed. Not using keys can lead to inefficient rendering.

// Anti-pattern
const listItems = items.map((item) => <li>{item}</li>);
// Correct approach
const listItems = items.map((item) => <li key={item.id}>{item}</li>);

Overusing inline functions in render

Defining functions inside the render method can lead to performance issues because a new function is created on every render.

// Anti-pattern
render() {
return <button onClick={() => this.handleClick()}>Click me</button>;
}
// Correct approach
render() {
return <button onClick={this.handleClick}>Click me</button>;
}

Deeply nested state

Deeply nested state can make state management complex and error-prone. Flatten the state structure when possible.

// Anti-pattern
this.state = {
user: {
profile: {
name: 'John',
age: 30,
},
},
};
// Correct approach
this.state = {
userName: 'John',
userAge: 30,
};

Further reading

How do you decide between using React state, context, and external state managers?

Topics
React

TL;DR

Choosing between React state, context, and external state managers depends on the complexity and scope of your application's state management needs. Use React state for local component state, React context for global state that needs to be shared across multiple components, and external state managers like Redux or MobX for complex state management that requires advanced features like middleware, time-travel debugging, or when the state needs to be shared across a large application.


Deciding between React state, context, and external state managers

React state

React state is best suited for managing local state within a single component. It is simple to use and provides a straightforward way to handle state that does not need to be shared across multiple components.

When to use React state
  • When the state is only relevant to a single component
  • When the state does not need to be accessed or modified by other components
  • When you want to keep the component self-contained and easy to understand
Example
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

React context

React context is useful for sharing state across multiple components without having to pass props down through every level of the component tree. It is ideal for global state that needs to be accessed by many components.

When to use React context
  • When you need to share state across multiple components
  • When you want to avoid prop drilling (passing props through many levels of components)
  • When the state is relatively simple and does not require advanced state management features
Example
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemedComponent() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<div>
<p>Current theme: {theme}</p>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</div>
);
}
function App() {
return (
<ThemeProvider>
<ThemedComponent />
</ThemeProvider>
);
}

External state managers

External state managers like Redux or MobX are designed for complex state management needs. They provide advanced features such as middleware, time-travel debugging, and the ability to manage state across a large application.

When to use external state managers
  • When the state management needs are complex and involve many interconnected pieces of state
  • When you need advanced features like middleware, time-travel debugging, or dev tools
  • When the state needs to be shared across a large application with many components
Example with Redux
// actions.js
export const increment = () => ({ type: 'INCREMENT' });
// reducer.js
const initialState = { count: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
default:
return state;
}
}
export default counterReducer;
// store.js
import { createStore } from 'redux';
import counterReducer from './reducer';
const store = createStore(counterReducer);
export default store;
// Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './actions';
function Counter() {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>Increment</button>
</div>
);
}
// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './Counter';
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}

Further reading

Explain the composition pattern in React

Topics
React

TL;DR

The composition pattern in React is a way to build components by combining smaller, reusable components. Instead of using inheritance, React encourages composition to create complex UIs. You can pass components as children or props to other components to achieve this. For example:

function WelcomeDialog() {
return (
<Dialog>
<h1>Welcome</h1>
<p>Thank you for visiting our spacecraft!</p>
</Dialog>
);
}
function Dialog(props) {
return <div className="dialog">{props.children}</div>;
}

Composition pattern in React

What is composition?

Composition is a design principle that involves combining smaller, reusable components to build more complex components. In React, this is preferred over inheritance for creating complex UIs.

How to use composition in React

Passing components as children

One common way to use composition is by passing components as children to other components. This allows you to nest components and create a hierarchy.

function Dialog(props) {
return <div className="dialog">{props.children}</div>;
}
function WelcomeDialog() {
return (
<Dialog>
<h1>Welcome</h1>
<p>Thank you for visiting our spacecraft!</p>
</Dialog>
);
}
Passing components as props

Another way to achieve composition is by passing components as props. This allows for more flexibility and customization.

function SplitPane(props) {
return (
<div className="split-pane">
<div className="split-pane-left">{props.left}</div>
<div className="split-pane-right">{props.right}</div>
</div>
);
}
function App() {
return <SplitPane left={<Contacts />} right={<Chat />} />;
}

Benefits of composition

  • Reusability: Smaller components can be reused across different parts of the application.
  • Maintainability: Easier to manage and update smaller components.
  • Flexibility: Components can be easily combined in different ways to create complex UIs.

When to use composition

  • When you need to create complex UIs from smaller, reusable components.
  • When you want to avoid the pitfalls of inheritance, such as tight coupling and difficulty in managing state.

Further reading

What is React Suspense and what does it enable?

Topics
React

TL;DR

React Suspense is a feature that allows you to handle asynchronous operations in your React components more gracefully. It enables you to show fallback content while waiting for something to load, such as data fetching or code splitting. You can use it with React.lazy for code splitting and with libraries like react-query for data fetching.

const LazyComponent = React.lazy(() => import('./LazyComponent'));
function MyComponent() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>
);
}

What is React Suspense and what does it enable?

Introduction to React Suspense

React Suspense is a feature introduced by the React team to help manage asynchronous operations in a more declarative way. It allows you to specify a loading state (fallback) while waiting for some asynchronous operation to complete, such as data fetching or code splitting.

Code splitting with React.lazy

One of the primary use cases for React Suspense is code splitting. Code splitting allows you to load parts of your application on demand, which can significantly improve the initial load time of your application.

import React, { Suspense } from 'react';
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function MyComponent() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}

In this example, React.lazy is used to dynamically import the LazyComponent. The Suspense component wraps the lazy-loaded component and provides a fallback UI (<div>Loading...</div>) to display while the component is being loaded.

Data fetching with Suspense

React Suspense can also be used for data fetching, although this feature is still experimental and requires additional libraries like react-query or Relay.

import React, { Suspense } from 'react';
import { useQuery } from 'react-query';
function fetchData() {
return fetch('https://api.example.com/data').then((response) =>
response.json(),
);
}
function DataComponent() {
const { data } = useQuery('data', fetchData);
return <div>{data}</div>;
}
function MyComponent() {
return (
<Suspense fallback={<div>Loading data...</div>}>
<DataComponent />
</Suspense>
);
}

In this example, react-query is used to fetch data, and Suspense provides a fallback UI while the data is being loaded.

Benefits of React Suspense

  • Improved user experience: By showing fallback content, you can keep the user engaged while waiting for asynchronous operations to complete.
  • Simplified code: Suspense allows you to handle loading states declaratively, reducing the need for complex state management.
  • Better performance: Code splitting with React.lazy can significantly reduce the initial load time of your application.

Further reading

Explain what happens when the `useState` setter function is called in React

Topics
React

TL;DR

When the setter function returned by the useState hook is called in React, it schedules an update to the component's state value. React then queues a re-render of the component with the new state. This process is typically asynchronous, and React batches multiple state updates together for performance.


What happens when the useState setter is called

State update scheduling

When you call the setter function provided by useState (e.g., setCount), React schedules an update for that specific state variable. This doesn't happen instantly; React marks the component as needing to re-render with the updated state value.

const [count, setCount] = useState(0);
// ...
setCount(count + 1); // Schedules an update to set 'count' to 1

State replacement

The useState setter function replaces the old state value entirely with the new value you provide. If your state is an object and you only want to update one property, you need to manually spread the old state and override the specific property.

const [user, setUser] = useState({ name: 'Anon', age: 99 });
// To update only name, you must spread the old state:
setUser((prevState) => ({ ...prevState, name: 'John' }));
// If you just did setUser({ name: 'John' }), the 'age' property would be lost.

Re-rendering

After scheduling the state update(s), React will eventually trigger a re-render of the component. The functional component body is executed again with the new state value(s). React updates its virtual DOM, compares it with the previous version, and efficiently updates the actual DOM only where necessary.

Asynchronous nature and Batching

State updates triggered by useState setters are typically asynchronous and batched. If you call multiple state setters in the same event handler or effect, React will often batch these updates together into a single re-render pass for better performance. Because of this, you shouldn't rely on the state variable having its new value immediately after calling the setter. If the new state depends on the previous state, use the functional update form.

// Assume count is 0
setCount(count + 1); // Queues update to 1
setCount(count + 1); // Still sees count as 0, queues update to 1 again!
// Result might be 1, not 2
// Correct way using functional update:
setCount((prevCount) => prevCount + 1); // Queues update based on previous state
setCount((prevCount) => prevCount + 1); // Queues another update based on the result of the first
// Result will be 2

Further reading

What does re-rendering mean in React?

Topics
React

TL;DR

Re-rendering in React refers to the process where a component updates its output to the DOM in response to changes in state or props. When a component's state or props change, React triggers a re-render to ensure the UI reflects the latest data. This process involves calling the component's render method again to produce a new virtual DOM, which is then compared to the previous virtual DOM to determine the minimal set of changes needed to update the actual DOM.


What does re-rendering mean in React?

Understanding re-rendering

Re-rendering in React is the process by which a component updates its output to the DOM in response to changes in its state or props. This ensures that the UI is always in sync with the underlying data.

When does re-rendering occur?

Re-rendering occurs in the following scenarios:

  • When a component's state changes using setState
  • When a component receives new props from its parent component
  • When the parent component re-renders, causing its child components to re-render as well

The re-rendering process

  1. State or props change: When a component's state or props change, React schedules a re-render for that component.
  2. Render method: React calls the component's render method to generate a new virtual DOM tree.
  3. Virtual DOM comparison: React compares the new virtual DOM tree with the previous one using a diffing algorithm.
  4. DOM updates: React calculates the minimal set of changes required and updates the actual DOM accordingly.

Example

Here's a simple example to illustrate re-rendering:

import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

In this example:

  • The Counter component has a state variable count.
  • When the button is clicked, setCount updates the state, triggering a re-render.
  • The render method is called again, and the new virtual DOM is compared to the previous one.
  • React updates the actual DOM to reflect the new count.

Performance considerations

Re-rendering can be expensive, especially for complex components or large applications. To optimize performance, React provides several techniques:

  • PureComponent: A base class that implements a shallow comparison of props and state to prevent unnecessary re-renders.
  • React.memo: A higher-order component that memoizes the result of a component's render to avoid re-rendering if the props haven't changed.
  • useMemo and useCallback: Hooks that memoize values and functions to prevent unnecessary re-renders.

Further reading

Why does React recommend against mutating state?

Topics
React

TL;DR

React recommends against mutating state because it can lead to unexpected behavior and bugs. React relies on state immutability to efficiently determine when to re-render components. When state is mutated directly, React may not detect the changes, leading to stale or incorrect UI updates. Instead, always create a new state object using methods like setState or the useState hook.


Why does React recommend against mutating state?

React's rendering mechanism

React uses a virtual DOM to optimize rendering. When state changes, React compares the new virtual DOM with the previous one to determine the minimal set of changes needed to update the actual DOM. This process is called reconciliation.

Immutability and state comparison

React relies on immutability to efficiently detect changes. When state is immutable, React can quickly determine if a component needs to re-render by comparing references. If the references are different, React knows that the state has changed.

Problems with mutating state

  1. Stale UI updates: Directly mutating state can lead to React not detecting changes, resulting in the UI not updating as expected.
  2. Debugging difficulties: Mutating state directly can make it harder to track state changes and debug issues.
  3. Unexpected behavior: Mutating state can lead to unpredictable behavior, especially when multiple components depend on the same state.

How to update state correctly

Instead of mutating state directly, always create a new state object. For example, when using the useState hook:

const [state, setState] = useState(initialState);
// Incorrect: Directly mutating state
state.value = newValue;
// Correct: Creating a new state object
setState({ ...state, value: newValue });

When using class components and setState:

this.setState((prevState) => ({
...prevState,
value: newValue,
}));

Further reading

Explain what React hydration is

Topics
React

TL;DR

React hydration is the process of attaching event listeners and making a server-rendered HTML page interactive on the client side. When a React application is server-side rendered, the HTML is sent to the client, and React takes over to make it dynamic by attaching event handlers and initializing state. This process is called hydration.


What is React hydration?

Server-side rendering (SSR)

Server-side rendering (SSR) is a technique where the HTML of a web page is generated on the server and sent to the client. This allows for faster initial page loads and better SEO since the content is already available when the page is loaded.

Hydration process

Hydration is the process that happens after the server-side rendered HTML is sent to the client. React takes the static HTML and "hydrates" it by attaching event listeners and initializing the state, making the page interactive. This process involves:

  1. Reusing the existing HTML: React uses the HTML generated by the server and does not re-render it from scratch.
  2. Attaching event listeners: React attaches the necessary event listeners to the existing HTML elements.
  3. Initializing state: React initializes the component state and props to make the page dynamic.

Example

Here's a simple example to illustrate the concept:

  1. Server-side rendering: The server generates the following HTML:

    <div id="root">
    <button>Click me</button>
    </div>
  2. Client-side hydration: When the HTML is sent to the client, React hydrates it with the following code:

    import React from 'react';
    import ReactDOM from 'react-dom';
    function App() {
    const handleClick = () => {
    alert('Button clicked!');
    };
    return <button onClick={handleClick}>Click me</button>;
    }
    ReactDOM.hydrate(<App />, document.getElementById('root'));

In this example, the server sends the static HTML with a button to the client. React then hydrates the button by attaching the onClick event listener, making it interactive.

Benefits of hydration

  1. Faster initial load: Since the HTML is already available, the initial page load is faster.
  2. SEO benefits: Search engines can crawl the server-rendered HTML, improving SEO.
  3. Improved user experience: Users can see the content immediately, even before React has fully taken over.

Challenges of hydration

  1. Mismatch issues: If the server-rendered HTML does not match the client-side React components, it can cause errors and warnings.
  2. Performance overhead: Hydration can be resource-intensive, especially for large applications.

Further reading

What is the difference between state and props in React?