In React, design patterns are general reusable solutions to common problems
This pattern involves separating components into two types -
Benifits: Improves code organization and maintainability.
HOCs are functions that take a component and return a new enhanced component, often used for code reuse, logic abstraction, and cross-cutting concerns.
Benifits: Benefits: Reusability and separation of concerns.
Higher-Order Components with Hooks
A component that takes a function as a prop, and calls it to render its output, allowing the sharing of code between components.
Benifits: Component composition and code sharing.
Uses the Context API to provide data or functionality to deeply nested components without passing props through each level.
Benifits: Avoids prop drilling and makes state management more straightforward.
Uses the Redux library for managing state in larger applications with complex state logic.
Benifits: i) Centralized state management, ii) time-travel debugging, iii) and predictable state changes.
Involves breaking down a component into smaller, more manageable components that work together.
Benifits: i) Encapsulation of logic, ii) improved readability, and maintainability.
Uses React error boundaries to catch JavaScript errors anywhere in a component tree and log those errors or display a fallback UI.
Benifits: Prevents the entire application from crashing due to a single component error.
Immutability is a principle where state cannot be directly modified; instead, a new copy is created with the desired changes.
Benifits: i) Predictable state changes, ii) improved performance in certain scenarios, iii) and simpler debugging
Involves rendering different content based on certain conditions.
Benifits: Allows for dynamic UI based on state or other factors.
Involves lifting the state up to a common ancestor when multiple components need to share the same state.
Benifits: Centralizes state management and reduces the complexity of passing state through props.
Refers to components where the value of form elements is controlled by React state.
Benifits: Enables dynamic and interactive forms in React.
Refers to form elements whose state is not controlled by React but by the DOM itself.
Benifits: Can be simpler in some cases, especially with certain form interactions.
Handles asynchronous operations in React components, often in combination with useEffect and useState.
Benifits: Enables handling data fetching and other asynchronous tasks in a declarative manner.
Represents component state and transitions as a finite state machine, often implemented with libraries like XState.
Benifits: Improves the clarity and predictability of state transitions in complex components.
Similar to Render Props but involves passing a callback function as a prop to handle rendering logic.
Benifits: Provides flexibility in rendering logic, especially useful for custom rendering scenarios.
Organizes components into a hierarchy based on atomic design principles (atoms, molecules, organisms, etc.).
Benifits: Improves scalability and maintainability by encouraging a consistent and modular component structure.
Encourages the use of immutable data structures and writing pure components that do not modify their props or state directly.
Benifits: Enhances predictability and performance by leveraging React's PureComponent and memoization.
functional components can now use lifecycle-like methods through the useEffect Hook.
The useEffect Hook provides a flexible way to manage side effects, handle asynchronous operations, and mimic the behavior of lifecycle methods in class components.
Here's an overview of how functional components handle lifecycle events using Hooks:
The equivalent to componentDidMount in functional components is achieved by using useEffect with an empty dependency array. Code inside this effect runs once when the component mounts.
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Code to run on mount
console.log('Component mounted');
// Clean-up function (equivalent to componentWillUnmount)
return () => {
console.log('Component unmounted');
};
}, []); // Empty dependency array means run only once on mount
// Rest of the component logic
return My Component;
}
The equivalent to componentDidUpdate in functional components is achieved by using useEffect with a dependency array. The code inside the effect runs when the specified dependencies change.
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// Code to run on every update
console.log('Component updated');
}, [count]); // Run the effect when 'count' changes
// Rest of the component logic
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
The equivalent to componentWillUnmount is achieved by returning a cleanup function from the useEffect. This function runs when the component is unmounted.
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Code to run on mount
console.log('Component mounted');
// Clean-up function (equivalent to componentWillUnmount)
return () => {
console.log('Component unmounted');
};
}, []); // Empty dependency array means run only once on mount
// Rest of the component logic
return <div>My Component</div>;
}
Benifits: Fine-grained control over component lifecycle for specific use cases.
When discussing design patterns in a React interview, it's essential to provide concrete examples from your experience and explain how these patterns helped in solving specific problems or improving the overall structure and maintainability of your code.
I suggest, practice one design pattern in different ways and in different scenarios