# Higher Order Components (HOCs)

## A Simple Higher Order Component

Example of a simple HOC.

{% tabs %}
{% tab title="withFavoriteNumber.js" %}

```jsx
import React from "react"

// Defining a HOC called 'withFavoriteNumber' and passing all components from 'App' component
export function withFavoriteNumber(component) {
    const Component = component
    return function(props){
        //  {...props} allows us to pass all props from 'App' component to our new Higher Order Component
        // 'favNum' is our new component's props 
        return <Component favNum="7" {...props} />
    }  
}
```

{% endtab %}

{% tab title="App.js" %}

```jsx
import React from "react"
import {withFavoriteNumber} from "./withFavoriteNumber"

function App(props) {
    return (
        <div>
            {props.someProps} // output: Hi there!
            {props.favNum} // output: 7
        </div>
    )
}

// Passing App component within our HOC function
export default withFavoriteNumber(App)
```

{% endtab %}

{% tab title="index.js" %}

```jsx
import React from "react"
import ReactDOM from "react-dom"
import App from "./App"

// 'someProps' is our App component's props
ReactDOM.render(<App someProps="Hi there!" />, document.getElementById("root"))

```

{% endtab %}
{% endtabs %}

## Sharing State with HOC

We can use HOC to **achieve DRY principle** & **share logic** between different components.

{% tabs %}
{% tab title="Favorite.js" %}

```jsx
import React, {Component} from "react"
import {hocToggler} from "./HOCs/hocToggler"

function Favorite(props) {
    return (
        <div>
            <h3>Click heart to favorite</h3>
            <h1>
                <span 
                    onClick={props.toggle}
                >
                    {props.on ? "❤️" : "♡"}
                </span>
            </h1>
        </div>
    ) 
}

export default hocToggler(Favorite)
```

{% endtab %}

{% tab title="Menu.js" %}

```jsx
import React from "react"
import {hocToggler} from "./HOCs/hocToggler"

function Menu(props) {
    return (
        <div>
            <button onClick={props.toggle}>{props.on ? "Hide" : "Show"} Menu </button>
            <nav style={{display: props.on ? "block" : "none"}}>
                <h6>Signed in as Coder123</h6>
                <a>Your Profile</a>
                <a>Your Repositories</a>
                <a>Your Stars</a>
                <a>Your Gists</a>
            </nav>
        </div>
    ) 
}

export default hocToggler(Menu)
```

{% endtab %}

{% tab title="hocToggler.js" %}

```jsx
import React, {Component} from "react"

class Toggler extends Component{
    state = {
        on: false
    }
    
    toggle = () => {
        this.setState(prevState => {
            return {
                on: !prevState.on
            }
        })
    }
    
    render() {
        const {component: C, ...props} = this.props
        return (
            <C on={this.state.on} toggle={this.toggle} {...props} />
        )
    }
}

export function hocToggler(component){
    return function(props){
        return(
            <Toggler component={component} {...props} />
        )
    }
}
```

{% endtab %}
{% endtabs %}

HOC can even take default props.

{% tabs %}
{% tab title="Favorite.js" %}

```jsx
import React from "react"
import {hocToggler} from "./hocToggler"

// Functional component
function Favorite(props){
    return(
        <div>
            <h3>Click heart to favorite</h3>
            <h1>
                // Deriving method from HOC
                <span 
                    onClick={props.toggle} 
                >
                // Deriving state from HOC
                    {props.on ? "❤️" : "♡"}
                </span>
            </h1>
        </div>
    ) 
}

// Passing components & optionObjects to HOC
export default hocToggler(Favorite, {defaultOnValue:false})
```

{% endtab %}

{% tab title="Menu.js" %}

```jsx
import React from "react"
import {hocToggler} from "./hocToggler"

function Menu(props) {
    return (
        // Deriving state & method from HOC
        <div>
            <button onClick={props.toggle}>{props.on ? "Hide" : "Show"} Menu </button>
            <nav style={{display: props.on ? "block" : "none"}}>
                <h6>Signed in as Coder123</h6>
                <a>Your Profile</a>
                <a>Your Repositories</a>
                <a>Your Stars</a>
                <a>Your Gists</a>
            </nav>
        </div>
    )    
}

// Passing components & optionObjects to HOC
export default hocToggler(Menu, {defaultOnValue:true})
```

{% endtab %}

{% tab title="hocToggler.js" %}

```jsx
import React, {Component} from "react"

class Toggler extends Component{
    // Shared state for both 'Favorite' & 'Menu' components
    state = {
        on: this.props.defaultOnValue
    }
    
    // Shared method for both 'Favorite' & 'Menu' components
    toggle = () => {
        this.setState((prevState)=>{
            return {
                on: !prevState.on
            }
        })
    }
    
    render(){
        // Functional component's properties are passed down here
        const Component = this.props.component
        
        return(
            // Creating a new supercharged component with Functional component's properties with HOC's own state & method
            <Component on={this.state.on} toggle={this.toggle} {...this.props} />
        )
        
    }
}

// HOC functions
export function hocToggler(component, optionsObj){ // `component` parameter refers to `favorite` & `menu` components
    return function(props){
        // Receiving Functional component's properties
        return(
            <Toggler component={component} defaultOnValue={optionsObj.defaultOnValue} {...props} />
        )
    }
}
```

{% endtab %}

{% tab title="App.js" %}

```jsx
import React from "react"
import Menu from "./Menu"
import Favorite from "./Favorite"

function App() {
    return (
        <div>
            <Menu />
            <hr />
            <Favorite />
        </div>
    )
}

export default App
```

{% endtab %}

{% tab title="index.js" %}

```jsx
import React from "react"
import ReactDOM from "react-dom"
import App from "./App"

ReactDOM.render(<App />, document.getElementById("root"))
```

{% endtab %}
{% endtabs %}
