Why Does React Component Unmount and remount on state change?

Viewed 3097

Why does my HomeTypes component unmount and remount every time I use setDrawerOpen? Does a state change inside a component always cause that component to unmount and then re-mount?

import React, { useEffect, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import Drawer from '../../components/DrawerComponent/Drawer';

function HomeTypes() {
    const [drawerOpen, setDrawerOpen] = useState(null);

    useEffect(() => {
        console.log('Home Types Mounted');
        return () => {
            console.log('Home Types Unmounted');
        };
    });

    return (
        <div className={`dashboard-page`}>
            <h1 className="dashboard-page-title">
                Home Types
                <button
                    className={`btn bright primary`}
                    onClick={() => {
                        setDrawerOpen(true);
                    }}>
                    <FontAwesomeIcon icon={['fas', 'plus']} />
                    <span>add home type</span>
                </button>
            </h1>
            <Drawer
                drawerOpen={drawerOpen}
                closeDrawer={() => {
                    setDrawerOpen(false);
                }}
                title="Add Home Type"
                drawerContent="Hello World"
            />
        </div>
    );
}

export default HomeTypes;

2 Answers

You're missing a dependencies array in your useEffect, that might be causing the behavior

  useEffect(() => {
        console.log('Home Types Mounted');
        return () => {
            console.log('Home Types Unmounted');
        };
    }. []); // this array right here

documentation see the Note in orange below: Using the Effect Hook

The hook useEffect will run every time the component renders. to run the effect once on mount (end cleanup on dismount) run it with a second parameter [] in this form:

useEffect(() => {
  // Side Effect
  return () => {
    // Cleanup
  }
}, [])

Edit: Better to just quote the docs

Note

If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders. Learn more about how to deal with functions and what to do when the array changes too often.

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.

If you pass an empty array ([]), the props and state inside the effect will always have their initial values. While passing [] as the second argument is closer to the familiar componentDidMount and componentWillUnmount mental model, there are usually better solutions to avoid re-running effects too often. Also, don’t forget that React defers running useEffect until after the browser has painted, so doing extra work is less of a problem.

We recommend using the exhaustive-deps rule as part of our eslint-plugin-react-hooks package. It warns when dependencies are specified incorrectly and suggests a fix.

Related