React Design Patterns

Higher Order Components

In React, a Higher-Order Component (HOC) is a design pattern that allows you to reuse component logic. It's a function that takes a component and returns a new component with additional props or behavior. HOCs are a way to share code, state, or side-effects among multiple React components without using inheritance or duplicating code.

The main purpose of HOCs is to enhance or wrap existing components with some additional functionality. This can include providing data via props, managing state, adding event handlers, and more.

import React, {useState} from "react";
// Define a higher order compoent that adds a "counter" state
// and "increment" method to the wrapped component
const withCounter = (WrappedComponent) =>{
    return function WithCounter(props){
        const [counter, setCounter] = useState(0);
        const increment = () => setCounter(prev => prev + 1);
        return <WrappedComponent
                counter = {counter}
                increment = {increment}
                {...props}
                />
    };
};

// Create a component that uses the "counter" state & "increment" method
const CounterDisplay = ({counter, increment}){
    return <div>
                <h2>Counter</h2>
                <p>Count: {counter}</p>
                <button onClick = {increment}>+</button>
            </div>
}

//Wrap the CounterDisplay comepoent the withCounter HOC
const CounterDisplayWithCounter = withCounter(CounterDisplay);

// Useage of the Wrapped Component
const App = () =>{
    return <div>
                <h1>Higher Order Component</h1>
                <CounterDisplayWithCounter/>
            </div>
}
export default App;

In this example, The 'withCounter' HOC is created using a function component and the 'useState' hook to manage the 'counter' state and 'increment' function. It wraps the 'CounterDisplay' component, providing it with the 'counter' state and 'increment' function as props. The CounterDisplay component uses these props to display and update the counter.

Render Props Pattern

The Render Props Pattern is a way to share code and functionality between components in React. It involves passing a function as a prop to a component, and that function is responsible for rendering inside the component.

Essentially you are giving control of what gets rendered within a component to the parent component.

Example: Imagine you have a 'Toggle' component that can either show or hide its content. Instead of hardcoding the content inside the 'Toggle' component, you pass a function (the render prop) as a prop to 'Toggle'. This function decides what to render when the 'Toggle' is in the 'on' state.

// Create a Toggle component that can be 'on' & 'off'
function Toggle({render}){
    const [on, setOn] = useState(false);
    const toggle = () => setOn(prev => !prev);
    return <div>
                <button onClick = {toggle}>toggle</button>
                {render(on)}
            </div>
}

//Usage of the Toggle component with a render prop
function App(){
    return <div>
                <h1>Toggle Exmaple</h1>
                <Toggle
                    render = {(isOn) => (
                        <div>
                         {isOn ? <p>Toggle is on</p> : <p>Toggle is off</p>}
                        </div>
                    )}
                />
            </div>
}
export default App;

Compound Components Pattern

The Compound Components Pattern in React is a design pattern where a single-parent component manages the behavior and state of a group of child components, allowing them to work together closely.

Here's an example of the Compound Components Pattern👇

import React, {useContext, createContext} from "react";

// Create Context to manage state of a group of chiled components
const CounterContext = createContext();
// Create a parent component and provide context
function Counter({children}){
    const [counter, setCounter] = useState(0);
    const increment = () => setCounter(prev => prev + 1);
    const decrement = () => setCounter(prev => prev - 1);
    return <CounterContext.Provider
            values = {{counter, increment, decrement}}
            >
                {children}
            </CounterContext.Provider>
}

// Create related child components
function Count(){
    const {counter} = useContext(CounterContext);
    return <span>{counter}</span>
}

function Increment({icon = "+"}){
    const {increment} = useContext(CounterContext);
    return <button onClick = {increment}>{icon}</button>
}

function Decrement({icon = "-"}){
    const {decrement} = useContext(CounterContext);
    return <button onClick = {decrement}>{icon}</button>
}

// Optional step
Counter.Count = Count;
Counter.Increment = Increment;
Counter.Decrement = Decrement;

// Usage of Counter Component
function App(){
    return <Counter>
                <Counter.Decrement/>
                <Counter.Count/>
                <Counter.Increment/>  
            </Counter>
}
export default App;

Thanks For Reading

If you have any query then please feel free to ask me in comment section🙂