How to properly use componentWillUnmount() in ReactJs

JavascriptReactjs

Javascript Problem Overview


From the official tutorial:

> componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any DOM elements that were created in componentDidMount

I understood "invalidating timers". fetch can be aborted with AbortController. But I don't understand "cleaning up any DOM elements that were created in componentDidMount", can I see examples for that case?

Javascript Solutions


Solution 1 - Javascript

If the network request sending library supports aborting the ongoing network request call, you can definitely call that in componentWillUnmount method.

However in relation to cleaning up of DOM elements is of concern. I will give a couple of examples, based on my current experience.

First one is -

import React, { Component } from 'react';

export default class SideMenu extends Component {

    constructor(props) {
        super(props);
        this.state = {
              };
        this.openMenu = this.openMenu.bind(this);
        this.closeMenu = this.closeMenu.bind(this);
    }

    componentDidMount() {
        document.addEventListener("click", this.closeMenu);
    }

    componentWillUnmount() {
        document.removeEventListener("click", this.closeMenu);
    }

    openMenu() {
    }

    closeMenu() {
    }

    render() {
        return (
            <div>
                    <a
                        href      = "javascript:void(0)"
                        className = "closebtn"
                        onClick   = {this.closeMenu}
                    >
                        ×
                    </a>
                  <div>
                     Some other structure
                  </div>
                </div>
        );
    }
}

Here I am removing the click event listener which I added when the component mounted.

Second one is -

import React from 'react';
import { Component } from 'react';
import ReactDom from 'react-dom';
import d3Chart from './d3charts';


export default class Chart extends Component {

    static propTypes = {
            data: React.PropTypes.array,
            domain: React.PropTypes.object
    };

    constructor(props){
        super(props);

    }

    componentDidMount(){
        let el = ReactDom.findDOMNode(this);
        d3Chart.create(el, {
            width: '100%',
            height: '300px'
        }, this.getChartState());
    }

    componentDidUpdate() {
        let el = ReactDom.findDOMNode(this);
        d3Chart.update(el, this.getChartState());
    }

    getChartState() {
        return {
            data: this.props.data,
            domain: this.props.domain
        }
    }

    componentWillUnmount() {
        let el = ReactDom.findDOMNode(this);
        d3Chart.destroy(el);
    }

    render() {
        return (
            <div className="Chart">
            </div>
        );
    }
}

Here I am trying to integrate d3.js with react into componentWillUnmount; I am removing the chart element from the DOM.

Apart from that I have used componentWillUnmount for cleaning up bootstrap modals after opening.

I am sure there are tons of other use cases out there, but these are the cases where I have used componentWillUnMount. I hope it helps you.

Solution 2 - Javascript

In this simple example (example taken from React Docs) I am using it for clearing interval of a Clock component. For example in your page you have 2 tabs, one of them is showing User Info and second tab is User Schedule which shows a live clock over there. Once you switch to User Schedule tab, componentDidMount will be called to set the timer. And once you switch back to User Info, there is no need to keep this interval hook and you can write your unbind/unsubscribe logic in componentWillUnmount event.

import React from "react";
export default class Clock extends React.Component {
  constructor(props) {
    console.log("Clock", "constructor");
    super(props);   
    this.state = {
      date: new Date()
    };
  }
  tick() {   
    this.setState({
      date: new Date()
    });
  }
  // These methods are called "lifecycle hooks".
  componentDidMount() {
    console.log("Clock", "componentDidMount");
    this.timerID = setInterval(() => {
      this.tick();
    }, 1000);
  }
  // These methods are called "lifecycle hooks".
  componentWillUnmount() {
    console.log("Clock", "componentWillUnmount");
    clearInterval(this.timerID);
  }
  render() {
    return (        
        <div>It is {this.state.date.toLocaleTimeString()}.</div>
    );
  }
}

Solution 3 - Javascript

When creating Components with React, not every library integrates well with it's philosophy of wanting to manage the DOM.

An example of this would be using a graphing library like c3. c3 expects to be given a DOM node and will create / manage it's own markup away from React. In this case you should manage cleaning up any elements created by this library when your component is removed from the DOM.

import React, { Component, PropTypes } from 'react';
import c3 from 'c3';

export default class Graph extends Component {

  componentDidMount () {
    this._initGraph();
  }

  componentWillUnmount () {
    this.graph = this.graph.destroy();
  }

  _initGraph () {
    this.graph = c3.generate({
      bindto: this.refs.graph
    });
  }

  render () {
    return (
      <div className="graph">
        <div className="graph__graph" ref="graph"></div>
      </div>
    );
  }
}

Here React creates a single div as a placeholder for c3 to add it's content. This process is kicked off in the componentDidMount lifecycle hook, and cleaned up again in componentWillUnmount.

Solution 4 - Javascript

Simple solution is as follows:

import React from "react"
export default class App extends React.Component {

  isMounted = false

  componentDidMount(){
    this.isMounted = true
    if (this.isMounted){
      this.setState({'anyState': anyState})  //call setState if component isMounted
    }
  }

  componentWillUnmount(){
    this.isMounted = false
  }

  render(){
    return(
      <div />
    )
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAli AnkaraliView Question on Stackoverflow
Solution 1 - JavascriptWitVaultView Answer on Stackoverflow
Solution 2 - JavascriptTeoman shipahiView Answer on Stackoverflow
Solution 3 - JavascriptAshley 'CptLemming' WilsonView Answer on Stackoverflow
Solution 4 - JavascriptTushar SharmaView Answer on Stackoverflow