Welcome to the React Design Patterns Exploration repository. This project serves as a sandbox to explore and understand different design patterns commonly used in React applications. Each design pattern is explained, and examples are provided to illustrate their implementation.
- Container/Component Pattern
- Presentational/Component Pattern
- Render Props
- Higher-Order Components (HOCs)
- Hooks
The Container/Component pattern involves dividing components into two categories: container components and presentational components. Container components manage data logic and state, while presentational components focus solely on rendering UI.
- Clean separation of concerns.
- Improved code organization and maintainability.
- Enhanced component reusability.
- Facilitates easier testing.
- Slight increase in boilerplate code due to separate container components.
Ideal for larger applications where data management and UI rendering can be decoupled. Promotes reusability and maintainability.
Check ContainerComponent.js and PresentationalComponent.js.
// ContainerComponent.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchData } from '../actions';
import PresentationalComponent from './PresentationalComponent';
class ContainerComponent extends Component {
componentDidMount() {
this.props.fetchData();
}
render() {
return <PresentationalComponent data={this.props.data} />;
}
}
const mapStateToProps = state => ({
data: state.data
});
export default connect(mapStateToProps, { fetchData })(ContainerComponent);
// PresentationalComponent.js
import React from 'react';
const PresentationalComponent = ({ data }) => (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
export default PresentationalComponent;
An extension of the container/component pattern, the Presentational/Component pattern further divides components into presentational and container components. Presentational components handle UI rendering, while container components handle data logic.
- Clear separation of concerns.
- High component reusability.
- Improved code readability.
- May result in a larger number of small components, which could be overwhelming in small projects.
Ideal for projects with complex UIs that require a high level of reusability and modularity.
Check PresentationalComponent.js and ContainerComponent.js.
import React from 'react';
const PresentationalComponent = ({ data }) => (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
import React, { Component } from 'react';
import { fetchData } from '../actions';
import PresentationalComponent from './PresentationalComponent';
class ContainerComponent extends Component {
componentDidMount() {
this.props.fetchData();
}
render() {
return <PresentationalComponent data={this.props.data} />;
}
}
export default ContainerComponent;
export default PresentationalComponent;
The Render Props pattern is a technique used in React where a component receives a function as a prop. This function, known as the render function, provides content that the component will render. It's a way to share behavior between components without relying on inheritance or complex component hierarchies.
- Highly flexible and dynamic approach to component composition.
- Promotes code sharing and reusability, as multiple components can use the same render function.
- Allows decoupling of behavior from components, leading to cleaner and more maintainable code.
- May lead to nested components and layers of indentation if not managed properly.
- Can introduce a learning curve for those unfamiliar with the pattern.
Effective for scenarios where you want to share behavior between components that don't have a direct parent-child relationship. It's particularly useful when you need dynamic rendering and customization.
Check RenderPropsComponent.js.
import React from 'react';
const RenderPropsComponent = ({ renderFunction }) => (
<div>
{renderFunction('Hello from Render Props')}
</div>
);
export default RenderPropsComponent;
###Usage: You can use the RenderPropsComponent by passing a render function as a prop.
import React from 'react';
import RenderPropsComponent from './RenderPropsComponent';
const App = () => (
<div>
<RenderPropsComponent
renderFunction={content => <h1>{content}</h1>}
/>
</div>
);
export default App;
Higher-Order Components (HOCs) are a design pattern in React where functions are used to enhance components by adding extra functionality. HOCs take a base component and return an extended version of that component, allowing the addition of shared behaviors without altering the original component's implementation.
- Enables efficient code reuse and separation of concerns by encapsulating common functionality in HOCs.
- Modifies component behavior without requiring changes to the underlying component itself.
- Offers an organized approach to managing cross-cutting concerns.
- May lead to "wrapper hell," where multiple layers of nested HOCs can complicate the component hierarchy.
- Can increase the complexity of understanding component behavior due to the indirect nature of modifications.
Ideal for scenarios where you want to add consistent and reusable functionalities to multiple components across your application.
Check withLogger.js.
// withLogger.js
import React from 'react';
const withLogger = WrappedComponent => {
return class extends React.Component {
componentDidMount() {
console.log('Component did mount');
}
render() {
return <WrappedComponent {...this.props} />;
}
};
};
export default withLogger;
---
Usage:
Apply the HOC to a component to add logging behavior.
```jsx
jsx
Copy code
import React from 'react';
import withLogger from './withLogger';
const ComponentWithLogger = withLogger(MyComponent);
export default ComponentWithLogger;
Hooks are special functions in React that allow you to use state and other React features without writing class components. They provide a more concise and functional approach to managing state, lifecycle, and side effects in functional components.
- Simplifies component logic by encapsulating stateful and side-effectful code within functions.
- Avoids the complexity of class components and eliminates the need for constructor and lifecycle methods.
- Enhances code readability and maintainability by promoting a more straightforward component structure.
- Requires a mindset shift for developers accustomed to class components and traditional React practices.
- Some custom hooks might not be as performant as their class component equivalents, depending on how they're implemented.
Perfect for modern React applications that aim to efficiently manage state, lifecycle, and side effects. Hooks are particularly useful in functional components, making them a core feature of contemporary React development.
Check useCounter.js. Custom Hook: Here's an example of a custom hook for managing a simple counter.
// useCounter.js
import { useState } from 'react';
const useCounter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return { count, increment };
};
export default useCounter;
Usage: Use the useCounter custom hook in a functional component.
Copy code
import React from 'react';
import useCounter from './useCounter';
const CounterComponent = () => {
const { count, increment } = useCounter();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
export default CounterComponent;
- Clone this repository.
- Navigate to each example folder and explore the patterns.
- Run
npm install
andnpm start
within each example folder to see the patterns in action.