React is an open-source JavaScript library developed by Facebook for building user interfaces, particularly single-page applications. It enables developers to create reusable, composable UI components using a declarative approach. React helps solve the problem of efficiently updating and rendering components when the application state changes, by using a virtual DOM to minimize the number of updates to the actual DOM, thus improving performance.
Class components are created using ES6 classes and have built-in state and lifecycle methods, while functional components are plain JavaScript functions that can utilize hooks to manage state and side effects.
Props (short for "properties") are a way to pass data from a parent component to its child components. They are read-only and should not be modified within the child component.
State is an object that holds the internal data of a component. Unlike props, state is mutable and can be updated within the component itself using the setState method (in class components) or the useState hook (in functional components).
The most important lifecycle methods are:
componentDidMount
: Invoked after the component is mounted to the DOM. Commonly used for making API calls and setting up subscriptions.componentDidUpdate
: Invoked after the component has been updated. Useful for handling updates based on previous and current props or state.componentWillUnmount
: Invoked before the component is removed from the DOM. Typically used for cleaning up subscriptions, timers, or other resources.React hooks are functions that allow functional components to manage state, side effects, and access lifecycle-like behavior. They were introduced to simplify the use of state and lifecycle features without needing to use class components. Some common hooks are useState, useEffect, and useContext.
The key prop
is a unique identifier that helps React keep track of which items in a list have changed, been added, or been removed. It improves performance during reconciliation, making it easier for React to identify which elements need to be updated or removed.
The Context API provides a way to share data across the component tree without passing it through props. It consists of a React.createContext()
function that returns a context object with a Provider and a Consumer component, or using the useContext
hook.
In React, form inputs are usually handled using controlled components. The input's value is set using the component's state, and an event handler is used to update the state on user input. This way, the state always reflects the current input value.
Create a functional component that renders a counter with two buttons: one for incrementing and one for decrementing the count.
import React, { useState } from 'react';
function CounterApp() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
return (
Counter: {count}
);
}
export default CounterApp;
Stateful components have internal state, while stateless components do not. Stateful components can be either class components or functional components using the useState
hook. Stateless components are pure functional components that only rely on props for rendering.
A Higher-Order Component is a function that takes a component and returns a new component with additional props or behavior. HOCs are a way to reuse component logic and enhance components without modifying their implementation.
A PureComponent is a class component that implements the shouldComponentUpdate
lifecycle method with a shallow prop and state comparison. This helps to prevent unnecessary re-renders when the props and state have not changed, improving performance.
Render props are a technique for sharing code between components by passing a function as a prop. The function returns JSX to be rendered, allowing the parent component to control the rendering of the child component. Use render props when you want to share component behavior without using HOCs or when you need more flexibility in rendering.
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree and display a fallback UI instead of the crashed component. Error boundaries are implemented using the lifecycle methods static getDerivedStateFromError and componentDidCatch.
The useCallback
hook returns a memoized version of a callback function, ensuring that the function reference remains the same between renders if its dependencies have not changed. This helps prevent unnecessary re-renders in child components that rely on the callback as a prop. Use useCallback
when you need to pass a stable reference of a function to child components.
The useMemo
hook memoizes a computed value, ensuring that the value is only recomputed when its dependencies change. This helps prevent expensive calculations from running on every render. Use useMemo
when you have expensive calculations that depend on specific props or state values.
React Portals provide a way to render a child component outside of the parent component's DOM hierarchy while maintaining the parent-child relationship in the React component tree. Use portals for cases like modals, tooltips, or other UI elements that should appear on top of other content and not be constrained by the parent's styling or layout.
React.lazy
is a function that allows you to load components lazily, as needed, using dynamic imports. This helps reduce bundle size and improve initial load performance. Suspense is a component that wraps around lazy-loaded components and displays fallback content while the component is being loaded.
React Fiber is a reimplementation of the React core algorithm, focused on improving the responsiveness and perceived performance of complex applications. Its primary goal is to enable incremental rendering and provide better support for concurrent updates and asynchronous rendering, leading to smoother UIs and better user experience.
Controlled components have their state managed by React, with form data being handled by the component's state and an event handler updating the state. Uncontrolled components, on the other hand, maintain their state internally and rely on the DOM to store form data. While controlled components offer more control and flexibility, uncontrolled components can be simpler to implement in some cases.
Performance optimization in React applications can involve:
Server-side rendering involves rendering the initial HTML of a React application on the server before sending it to the client. This allows for faster initial load times, better SEO, and improved perceived performance. SSR can be implemented using frameworks like Next.js or custom server setups.
Global state management can be achieved using the Context API, Redux, or other state management libraries. These solutions allow you to centralize the application state, making it easier to manage and share data across components. When choosing a solution, it's essential to consider factors like the application's size, complexity, and team familiarity with the tools.
The React Profiler is a tool built into React DevTools that helps you measure the performance of your React components. It records information about component render times, updates, and the impact on the user interface. To use the Profiler, open React DevTools, select the Profiler tab, and start recording. You can then interact with your application and analyze the recorded performance data.
Side effects, such as data fetching, subscriptions, or manual DOM manipulation, can be managed using the useEffect
hook in functional components or lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount
in class components. It's important to ensure side effects are properly cleaned up to avoid memory leaks and performance issues.
React components can be tested using testing libraries such as Jest and React Testing Library or Enzyme. Jest provides a testing framework and assertion library, while React Testing Library and Enzyme offer utilities for testing React components. Tests can include unit tests for individual components, integration tests for component interactions, and end-to-end tests for the entire application flow.
Code splitting is a technique for breaking your application into smaller bundles, which are loaded on demand. This reduces the initial bundle size and improves load times. In a React application, you can implement code splitting using dynamic imports and React.lazy
with Suspense
. Webpack, the default bundler for Create React App, automatically supports code splitting when using dynamic imports.
Create a functional component that renders a list of items and an input field for searching. As the user types in the input field, the list should be filtered to show only the items that match the search term.
import React, { useState } from 'react';
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Fig', 'Grape', 'Lemon'];
function SearchList() {
const [searchTerm, setSearchTerm] = useState('');
const filteredItems = items.filter((item) =>
item.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div>
<input
type="text"
placeholder="Search"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<ul>
{filteredItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default SearchList;
This component demonstrates a simple search functionality using state and the filter
method on arrays.