I have this code:
const App: React.FC = () => {const [isOpen, setIsOpen] = React.useState(true);const [maxHeight, setMaxHeight] = React.useState();const wrapper = React.useRef<HTMLDivElement>(null);const content = React.useRef<HTMLDivElement>(null);const setElementMaxHeight = () => {if (content && content.current) {setMaxHeight(isOpen ? content.current.offsetHeight : 0);}};useEffect(() => {setElementMaxHeight();window.addEventListener("resize", setElementMaxHeight);return () => {window.removeEventListener("resize", setElementMaxHeight);};});const toggle = () => {setIsOpen(!isOpen);};return (<div><button onClick={toggle}><span className="nominal-result__expander fa" /></button><divclassName="nominal-results__list-wrapper"ref={wrapper}style={!!maxHeight ? { maxHeight: `${maxHeight}px` } : undefined }><div className="nominal-results__list" ref={content} /></div></div>);};const rootElement = document.getElementById("root");ReactDOM.render(<App />, rootElement);
This will add and remove an event handler on each render.
Is this necessarily bad and does this actually gain anything from being a hook?
This came up in a code review and I am saying it is bad because it adds and removes the event listener on every render.
Best Answer
For this exact case you're right because undefined
is passed as the dependencies of useEffect
.
This means useEffect
runs on every render and thus the event handlers will unnecessarily get detached and reattached on each render.
function listener() {console.log('click');}function Example() {const [count, setCount] = window.React.useState(0);window.React.useEffect(() => {console.log(`adding listener ${count}`);window.addEventListener("click", listener);return () => {console.log(`removing listener ${count}`);window.removeEventListener("click", listener);};}); // <-- because we're not passing anything here, we have an effect on each renderwindow.React.useEffect(() => {setTimeout(() => {setCount(count + 1);}, 1000)});return count;}window.ReactDOM.render(window.React.createElement(Example), document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script><script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script><div id="root"></div>
But if you explicitly declare no dependencies by passing in an empty array []
, useEffect
will only run once, thus making this pattern perfectly legitimate for event handler attachment.
function listener() {console.log('click');}function Example() {const [count, setCount] = window.React.useState(0);window.React.useEffect(() => {console.log(`adding listener ${count}`);window.addEventListener("click", listener);return () => {console.log(`removing listener ${count}`);window.removeEventListener("click", listener);};}, []); // <-- we can control for this effect to run only once during the lifetime of this componentwindow.React.useEffect(() => {setTimeout(() => {setCount(count + 1);}, 1000)});return count;}window.ReactDOM.render(window.React.createElement(Example), document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script><script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script><div id="root"></div>
The useEffect hook in React allows us to perform side effects in functional components. By default, it runs after every render. However, we can optimize its behavior by providing a dependency array. When we omit the dependency array, the useEffect hook runs on every render, which can have a negative impact on SEO.
When the useEffect hook is used without a dependency array, it will execute the effect function after every render. This can lead to unnecessary re-rendering of the component, which can affect the performance and SEO of the website.
It is important to note that the useEffect hook without a dependency array is not always bad. There are certain scenarios where we intentionally want the effect to run on every render. However, in most cases, we should provide a dependency array to optimize the useEffect hook.
By providing a dependency array, we can specify which values the effect depends on. The effect will only run if any of the values in the dependency array change. This allows us to control when the effect should run and avoid unnecessary re-renders.
In conclusion, using the useEffect hook without a dependency array can have a negative impact on SEO. It is important to carefully consider whether the effect needs to run on every render or if it can be optimized by providing a dependency array. By providing a dependency array, we can improve the performance and SEO of our React components.