Understanding HOCs in React (in Typescript)
Published: 2022-08-23 14:37:00

HOC stands for Higher-Order Components.
It means a component which takes in another component and returns an _enhanced_ component.

>  const HoC = Component => EnhancedComponent

The primary use case of HOC is to have a single place to sharing functionalities between components.
(**Think of HOC more like a function, than component**, this will be more clear as we go.)

Suppose, you have a requirement to calculate the innerWidth of browser viewport as it is resized and then do some computations/ui change.

For our example, we just want to display the window.innerWidth in "realtime".
![Demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c64objdmrsda1eupwz1m.gif)
To achieve this we can write a functional component and use the useEffect hook and attach a listener function which will update a state variable.
See code: https://github.com/abhidatta0/react-hoc-in-typescript/blob/master/src/ResizeComponent.tsx

Now, suppose we have to use this logic in another component.In that case, we have to duplicate our logic 😞 (which is obviously bad).

**What could be a better(or best) way?**
We can just abstract away this useEffect and listener code into another component and expose the innerWidth variable as a prop.
This "special" component is our Higher-Order Component.

withResize.tsx
```
const withResize = (Component: FC<any> )=> (props: any)=> {
    const  [innerWidth, setInnerWidth] = useState(0);

    const handleResize = ()=>{
     setInnerWidth(window.innerWidth);
    }
 
    useEffect(()=>{
     window.addEventListener('resize', handleResize);
 
     return ()=>{
       window.removeEventListener('resize', handleResize);
     }
    },[]); 

    return <Component {...props} windowInnerWidth={innerWidth} />
}

``` 

WithResizeUsage.tsx
```
const WithResizeUsage = ({name, windowInnerWidth}: {name: string, windowInnerWidth: number})=>{
    return (
        <div>Inner Width is {windowInnerWidth} and name is {name}</div>
    )
}

export default withResize(WithResizeUsage);
```

App.tsx
```
  <WithResizeUsage name="Developer" />
```
Let's analyze what we have written
- In App.tsx, we call WithResizeUsage with a name prop.
- withResize is an hoc, which takes in a Component which itself accepts some props.
- The return of withResize is a component with all its original props and the extra `windowInnerWidth` prop.
- In WithResizeUsage: we accept the `name` prop that we get from App.tsx and the `windowInnerWidth` that we get from `withResize` hoc.
- In WithResizeUsage, we wrap it with withResize in the last line `withResize(WithResizeUsage)` to bind it with withResize.
 
See code:
https://github.com/abhidatta0/react-hoc-in-typescript/blob/master/src/withResize.tsx

https://github.com/abhidatta0/react-hoc-in-typescript/blob/master/src/WithResizeUsage.tsx

**Notice how clean our WithResizeUsage component becomes.**


Let's look at how we can enhance the HOC a bit more.
Suppose, we need that that the displayed window.innerWidth is x amount more and x value will be configurable.If inner width is 90 and x is 3, we will display 93.

We will modify the HOC by adding another level to accept additional params.(This concept is called **function currying**)  

withResizeAdvanced.tsx
```
type Params =  {
  bumped: number;
}
const withResizeAdvanced = (params: Params)=> (Component: FC<any> )=> (props: any)=> {
    console.log(params);
    const  [innerWidth, setInnerWidth] = useState(0);

    const handleResize = ()=>{
     setInnerWidth(window.innerWidth+params.bumped);
    }
 
    useEffect(()=>{
     window.addEventListener('resize', handleResize);
 
     return ()=>{
       window.removeEventListener('resize', handleResize);
     }
    },[]); 

    return <Component {...props} windowInnerWidth={innerWidth} />
}
``` 

withResizeAdvancedUsage.tsx

```
import withResizeAdvanced from "./withResizeAdvanced";

const WithResizeUsage = ({name, windowInnerWidth}: {name: string, windowInnerWidth: number})=>{
    return (
        <div>Inner Width is {windowInnerWidth} and name is {name}</div>
    )
}

export default withResizeAdvanced({bumped: 5})(WithResizeUsage);
```

App.tsx
```
      <WithResizeAdvancedUsage name="Developer"/>
```
 
Everything is similar to earlier HOC, except we can pass a object which has a property `bumped` can we use to modify the innerWidth.

If we put earlier and the new component in App.tsx, we can visualize that for the later one, innerWidth is incremented by 5.
![HOC bumped](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6f0iuc02oajvfj3bzqek.gif)

Github repo: https://github.com/abhidatta0/react-hoc-in-typescript

Note:
- Make sure HOC have the general logic (like getting window.innerWidth) will be reused across other components.  
- An alternative to HOC pattern for function component is to use custom hooks. 

---