reactjsreact-hooksuse-statehigh-order-component

Can I use useState inside HOC (High order component)?


I have a HOC (High order component) that returns a functional component. I want to use useState but I get this error message:

“src/HOC.js Line 5:35: React Hook "useState" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks”

import React, { useState } from "react";

const HOC = (Component, data) => {
    return () => {
        const [count, setCount] = useState(data); // Error here
        const handleClick = () => {
            setCount(count + 1);
        };
        return (
            <Component
                countNumber={count}
                handleClick={handleClick}
            />
        )
    }
};

export default HOC;
import React from "react";
import HOC from "./HOC";

const LikesCount = ({countNumber, handleClick}) => {
    return (
        <div>
            {countNumber} <br />
            <button onClick={handleClick}>Like</button>
        </div>
    );
}

const EnhancedLikes = HOC(LikesCount, 5);

export default EnhancedLikes;

Is there any way to use useState in this HOC scenario?


Solution

  • As discussed in the comments, I'll post the custom hook counterpart for the HOC above:

    As far as I understand in your code, you want an HOC that you can just "hooked" to the component and it will perform a count.

    so, you can create a custom hook:

    const useCounter = (default = 0) => {
          const [count, setCount] = useState(default);
          const [add, setAdd] = useState(0);
          const handleClick = (base = 0) => {
              setCount(count + base + add);
          };
          const onSetAdd = (e) => setAdd(e.target.value);
          
          return {count, handleClick, onSetAdd};
    }
    

    now to hook it in a functional component:

    
    const LikesCount = () => {
        const {count, handleClick, onSetAdd} = useCounter(0);
        const onAddOne = () => handleClick(); // default is 1, so passing none = one
        const onAddTwo = () => handleClick(2);
        const onAddThree = () => handleClick(3);
       
    
        return (
            <div>
                <input type="number" onChange={onSetAdd} value={add} />
                {count} <br />
                <button onClick={onAddOne}>Like</button>
                <button onClick={onAddTwo}>Add 2</button>
                <button onClick={onAddThree}>Add 3</button>
            </div>
        );
    }
    
    export default LikesCount;
    

    That's it, you can just then add the ff code on any components that you want to have a counter:

    const {count, handleClick} = useCounter(0);
    

    The code above acts exactly the same as the sample HOC that you provided.