I'm trying to create a react app to demonstrate the working of HOC on functional components. I've two scenarios where a button and a heading display the number of clicks performed on the button and mouseovers on the heading with the same code internally. However, I get the error as mentioned. The error explains that the hook rule is broken. I could see that using hooks inside non-functional components is the incorrect way. I just wanted to know whether it is possible to use HOC that wraps functional components. Also, if it is possible to wrap functional components inside a HOC, then where do I change my code?
Here is my HOC code
import React, { useState } from "react";
const Count = (Component) => {
const [count, setCount] = useState(0);
const incrementHandler = (e) => {
setCount((prevState) => {
return prevState + 1;
});
};
return <Component count={count} incrementHandler={incrementHandler} />;
};
export default Count;
Here is my ClickCounter component
import React from "react";
import Count from "../HOC/CountHOC";
const ClickCounter = (props) => {
return (
<div>
<button onClick={props.incrementHandler}>
Clicked {props.count} times
</button>
</div>
);
};
export default Count(ClickCounter);
Here is my HoverCounter code
import React from "react";
import Count from "../HOC/CountHOC";
const HoverCounter = (props) => {
return (
<div>
<h1 onMouseOver={props.incrementHandler}>Hovered {props.count} times</h1>
</div>
);
};
export default Count(HoverCounter);
Yes, you can wrap function components just like you can wrap class components; the HOC doesn't know or care which kind you give it. If you look at the examples of HOCs in the documentation, you'll notice that they all return functions (in the case of that page, constructor functions):
// This function takes a component... function withSubscription(WrappedComponent, selectData) { // ...and returns another component... return class extends React.Component { // ... }; }
The result of a class expression is the constructor function of the class. A HOC function returns the new wrapper component it builds. Your function returns the result of React.createElement instead.
You need to return the enhanced component (whether a function or class component), rather than writing the HOC function itself as a component:
const withCount = (Component) => {
return function(props) {
const [count, setCount] = useState(0);
const incrementHandler = (e) => {
setCount((prevState) => {
return prevState + 1;
});
};
return <Component {...props} count={count} incrementHandler={incrementHandler} />;
};
};
The changes I made there are:
Count to withCount, since it's not a (simple) component, it's a HOC.Live Example:
const { useState } = React;
const withCount = (Component) => {
return function(props) {
const [count, setCount] = useState(0);
const incrementHandler = (e) => {
setCount((prevState) => {
return prevState + 1;
});
};
return <Component {...props} count={count} incrementHandler={incrementHandler} />;
};
};
const ClickCounter = (props) => {
return (
<div>
<button onClick={props.incrementHandler}>
Clicked {props.count} times
</button>
</div>
);
};
const EnhancedClickCounter = withCount(ClickCounter);
ReactDOM.render(<EnhancedClickCounter />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>