I would like to develop a new feature, which previously would have lived in a higher order component, using hooks. However, since according to the React Documentation:
"You can’t use Hooks inside of a class component, but you can definitely mix classes and function components with Hooks in a single tree."
So let's say I have some existing class component ExistingComponent
that I want to extend with additional functionality, say, listening to window.resize
. I would expect to do it like this.
// Existing Component
export class ExistingComponent extends React.Component {
render() {
return <div>I exist!</div>;
}
}
// Hook
export const useWindowResize = () => {
function resize() {
console.log('window resized!');
}
useEffect(() => {
window.addEventListener('resize', resize);
return function cleanup() {
window.removeEventListener('resize', resize);
};
});
};
// HOC
export const withWindowResize = component => {
useWindowResize();
return component;
};
// Extended Component
export const BetterComponent = withWindowResize(ExistingComponent);
However, this fails with Uncaught Invariant Violation: Hooks can only be called inside the body of a function component.
I do use react-hot-loader
, but I am still able to use hooks in component functions that don't return a class component. Also, I can remove the useWindowResize()
from the function and it renders as expected.
I am also able to render the example provided in the docs, so I know it's not a problem with hooks generically:
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Is this the wrong approach?
In a modern React world, everyone uses function components with React Hooks. However, the concept of higher-order components (HOC) is still applicable in a modern React world, because they can be used for class components and function components.
HOC is a function that takes a component as an argument and returns an enhanced version. HOC helps to isolate logic and state management in a separate class-based component. With React Hooks, state management can occur outside of a class. Hooks empower developers to use the functional programming aspects in React.
Both render props and higher-order components render only a single child but in most of the cases Hooks are a simpler way to serve this by reducing nesting in your tree.
You can't use Hooks inside a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component.
You can return a new function component from your withWindowResize
HOC in which you call the hook and spread the props on the component you pass in.
You can also pass an empty array as second argument to useEffect
to only have it run once after the initial render.
const { useEffect } = React;
class ExistingComponent extends React.Component {
render() {
return <div>I exist!</div>;
}
}
const useWindowResize = () => {
useEffect(() => {
function resize() {
console.log('window resized!');
}
window.addEventListener('resize', resize);
return () => {
window.removeEventListener('resize', resize);
};
}, []);
};
const withWindowResize = Component => {
return (props) => {
useWindowResize();
return <Component {...props} />;
}
};
const BetterComponent = withWindowResize(ExistingComponent);
ReactDOM.render(<BetterComponent />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With