React: Higher-Order Components

This time I get a sense of three use cases of React Higher-Order Components: wrapping with logs or so, shared logic on different components & same component with different data sources

higher-order component is a function that takes a component and returns a new component.

React: Higher-Order Components

Use case: Component wrapping

Let us say we have some component:

const SomeComp = ({value}) => {
    return <h1>{value}</h1>
}

But we want to add some logs or hooks around it. We can wrap it with a higher-order component.

const withEffect = (Component,props) => {
     return class extends React.Component {
        componentDidMount() {
            console.log(props);
        }
        render() {
            return <Component {...props} />
        }
    }
}

Or as a functional component:

const withEffect = (Component,props) => {
    return () => {
        React.useEffect(()=>{
            console.log(props)
        },[...props])
        return <Component {...props} />
    }
}

And since it’s a function, implement it like this:

const App = ()=>{
    const [someData,setSomeData] = React.useState({value:"hi"});
    const CompWithEffect = withEffect(SomeComp,someData);

    React.useEffect(()=>{
        setTimeout(()=>setSomeData({value:"hi dude"}),1000)
    },[]);

    return <>
        <CompWithEffect />
    </>
}

Use case: Shared logic

Let us say we have two different components with some shared logic. And I know, in this case, the simple solution is to pass the function, but allow me to use it as a simplistic example.

const RevertH1Comp = ({value}) => {
    const [revertValue,setRevertValue] = React.useState('');
    React.useEffect(()=>{
        setRevertValue(value.split('').reverse().join(''));
    },[value]);

    return <h1>{revertValue}</h1>
}

const RevertH2Comp = ({value}) => {
    const [revertValue,setRevertValue] = React.useState('');
    React.useEffect(()=>{
        setRevertValue(value.split('').reverse().join(''));
    },[value]);

    return <h2>{revertValue}</h2>
}

We can extract the shared logic into a Higher-Order component:

const withRevert = (Component,props) => {
    return () => {
        const [newProp,setNewProp] = React.useState(props)
        React.useEffect(()=>{
            setNewProp({...props, value: props.value.split('').reverse().join('')});
        },[props.value]);
        return <Component {...newProp} />
    }
}

And be left with:

const RevertH1Comp = ({value}) => {
    return <h1>{value}</h1>
}

const RevertH2Comp = ({value}) => {
    return <h2>{value}</h2>
}

Implementation:

const App = ()=>{
    const [value,setValue] = React.useState({value:"hi"});
    const H1CompWithEffect = withRevert(RevertH1Comp,value);
    const H2CompWithEffect = withRevert(RevertH2Comp,value);

    React.useEffect(()=>{
        setTimeout(()=>setValue({value:"hi dude"}),1000)
    },[]);

    return <>
        <H1CompWithEffect />
        <H2CompWithEffect />
    </>
}

Use case: Same component different usage

Now let’s say we have two different data sources:

const getSomeData = () => {
    return ["other","data"]
}
const getAsyncData = async () => {
    const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));
    
    await timeout(3000);
    return ["some","data"];
}

We want to display them using the same component:

const DisplaySomething = ({value}) => {
    return <>{value}</>
}

Once more, here comes a Higher-Order component to the rescue:

const conect = (Component,getVal) =>  {
    return () => {
        const [value,setValue] = React.useState([])
        React.useEffect(()=>{
            (async ()=>{
                const v = await getVal();
                setValue(v);
            })();
        },[]);
        return <Component value={value} />
    }
}

const App = ()=>{
    const SomeData = conect(DisplaySomething, getSomeData);
    const SomeAsyncData = conect(DisplaySomething, getAsyncData);

    return <>
        <SomeData />
        <SomeAsyncData />
    </>
}

Summery

Soon I may play the gong as loudly as possible