How to Unmount React Functional Component?
Asked Answered
F

3

7

I've built several modals as React functional components. They were shown/hidden via an isModalOpen boolean property in the modal's associated Context. This has worked great.

Now, for various reasons, a colleague needs me to refactor this code and instead control the visibility of the modal at one level higher. Here's some sample code:

import React, { useState } from 'react';
import Button from 'react-bootstrap/Button';
import { UsersProvider } from '../../../contexts/UsersContext';
import AddUsers from './AddUsers';

const AddUsersLauncher = () => {
  const [showModal, setShowModal] = useState(false);

  return (
    <div>
      <UsersProvider>
        <Button onClick={() => setShowModal(true)}>Add Users</Button>
        {showModal && <AddUsers />}
      </UsersProvider>
    </div>
  );
};

export default AddUsersLauncher;

This all works great initially. A button is rendered and when that button is pressed then the modal is shown.

The problem lies with how to hide it. Before I was just setting isModalOpen to false in the reducer.

When I had a quick conversation with my colleague earlier today, he said that the code above would work and I wouldn't have to pass anything into AddUsers. I'm thinking though that I need to pass the setShowModal function into the component as it could then be called to hide the modal.

But I'm open to the possibility that I'm not seeing a much simpler way to do this. Might there be?

Fleshpots answered 14/11, 2019 at 7:19 Comment(0)
N
1

If you want to leave showModal as state variable in AddUsersLauncher and change it from within AddUsers, then yes, you have to pass the reference of setShowModal to AddUsers. State management in React can become messy in two-way data flows, so I would advise you to have a look at Redux for storing and changing state shared by multiple components

Nisse answered 14/11, 2019 at 7:33 Comment(2)
Thanks for this. Your thinking about the need to pass setShowModal into AddUsers echoes my thinking but my colleague was insistent that this wasn't necessary. I'll ask him about it when he arrives this morning. As for Redux we've decided to not use it for this project and instead use React Context. So far we've found no problems with this alternate state management approach.Fleshpots
P.S. I was talking with my colleague again about this. He just hadn't thought through that we needed to eventually close the modal.Fleshpots
C
17

To call something on unmount you can use useEffect. Whatever you return in the useEffect, that will be called on unmount. For example, in your case

const AddUsersLauncher = () => {
  const [showModal, setShowModal] = useState(false);


  useEffect(() => {
    return () => {
      // Your code you want to run on unmount.
    };
  }, []); 


  return (
    <div>
      <UsersProvider>
        <Button onClick={() => setShowModal(true)}>Add Users</Button>
        {showModal && <AddUsers />}
      </UsersProvider>
    </div>
  );
};

Second argument of the useEffect accepts an array, which diff the value of elements to check whether to call useEffect again. Here, I passed empty array [], so, it will call useEffect only once.

If you have passed something else, lets say, showModal in the array, then whenever showModal value will change, useEffect will call, and will call the returned function if specified.

Carolinian answered 14/11, 2019 at 7:25 Comment(2)
Thank you for this. I was aware of the return in a useEffect as the cleanup function. But my colleague was insistent that nothing needed to be passed into <AddUsers /> in order to close it from within this child component. I couldn't see a way to do this, which is why I posted the question.Fleshpots
You have to be careful with the code in the clean up function in React 18+ with StrictMode because the component is mounted, unmounted and mounted again in dev-envAdalai
N
1

If you want to leave showModal as state variable in AddUsersLauncher and change it from within AddUsers, then yes, you have to pass the reference of setShowModal to AddUsers. State management in React can become messy in two-way data flows, so I would advise you to have a look at Redux for storing and changing state shared by multiple components

Nisse answered 14/11, 2019 at 7:33 Comment(2)
Thanks for this. Your thinking about the need to pass setShowModal into AddUsers echoes my thinking but my colleague was insistent that this wasn't necessary. I'll ask him about it when he arrives this morning. As for Redux we've decided to not use it for this project and instead use React Context. So far we've found no problems with this alternate state management approach.Fleshpots
P.S. I was talking with my colleague again about this. He just hadn't thought through that we needed to eventually close the modal.Fleshpots
F
0

With react 18 components will be mounted twice to test the stability in dev mode.

The logic we write on mount and unMount should respect this paradigm.

useMount

import { useEffect, useRef } from "react";

function useMount(run: () => void) {
  const referencedRun = useRef(run);
  useEffect(() => {
    referencedRun.current();
  }, []);
}

export default useMount;

useUnMount

import React, {
  DependencyList,
  EffectCallback,
  useEffect,
  useRef
} from "react";
import useMount from "./useMount";

const useUnMount = (effect: EffectCallback, dependencies?: DependencyList) => {
  const unMounted = useRef(false);

  // with react 18 component will be mounted twice in dev mode, so setting the reference to false on the second mount
  useMount(() => {
    unMounted.current = false;
  });

  // to identify unmount
  useEffect(
    () => () => {
      unMounted.current = true;
    },
    []
  );

  // wrap dependencies with the callback
  useEffect(
    () => () => {
      if (unMounted.current) {
        effect();
      }
    },
    [dependencies, effect]
  );
};

export default useUnMount;

Usage

  const [val, setVal] = useState("");

  useMount(() => {
    console.log("on mount...", val);
  });

  useUnMount(() => {
    console.log("on unmount...", val);
  }, [val]);

Notice that on unmount we are passing the dependencies to resolve the latest value.

refer: react-hooks-useeffect-cleanup-for-only-componentwillunmount

Fulmis answered 7/9, 2023 at 9:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.