Skip to content
Advertisement

Updating State in React Component causing it to get unmounted

I have a component where-in I need to fetch some data and render it. The component gets rendered initially. The problem I’m facing is when the handler function switchDocumentType is called after clicking the button for a particular type, the whole component gets unmounted/un-rendered.

While debugging on my own I found this happens after setDocumentType is run inside event handler function. What is wrong in the below code snippet that could possibly cause this issue? I can see the useEffect is not going in infinite-loop as well.

Code snippet:

import * as React from 'react';

const MyComponent = (props) => {
  const [documentType, setDocumentType] = React.useState('alpha');
  const [documentData, setDocumentData] = React.useState('');
  const types = ['alpha', 'beta', 'gamma'];

  React.useEffect(() => {
    myDataFetch('https://example.com/foo/?bar=123').then(async (response) => {
      const data = await response.json();
      setDocumentData(data.terms); // html string
      const myDiv = document.getElementById('spacial-div');
      myDiv.innerHTML = data; // need to render raw HTML inside a div
    });
  }, [documentType]);

  const switchDocumentType = (type) => {
    setDocumentType(type);
    // send some analytics events
  };

  const convertToPDF = () => {
    // uses documentData to generate PDF
  };

  return (
    <div className="container-div">
      {types.map((type) => {
        return (
          <button key={type} onClick={(type) => switchDocumentType(type)}>
            {type}
          </button>
        );
      })}
      <div id="special-div" />
    </div>
  );
};

export default MyComponent;

Advertisement

Answer

Not sure why but placing debuggers before state update causes this issue, not only for this component, but for all the other components I tried with. Seems to be an issue either with debugger or React. Removing debuggers solved the issue.

Also, now I’m returning a cleanup function inside useEffect as pointed out in some stack-overflow posts. I also refactored the code as suggested by @iaq and @sheepiiHD to follow React best practices.

Updated code:

import * as React from 'react';

const MyComponent = (props) => {
  const [documentType, setDocumentType] = React.useState('alpha');
  const [documentData, setDocumentData] = React.useState('');
  const types = ['alpha', 'beta', 'gamma'];

  const fetchData = async () => {
    const response = await myDataFetch('https://example.com/foo/?bar=123')
    const data = await response.json();
    setDocumentData(data);
  }

  React.useEffect(() => {
    fetchData();
    return () => {
      setDocumentType('');
      setDocumentData('');
    };
  }, []);

  const switchDocumentType = async (e, type) => {
    e.preventDefault();
    setDocumentType(type);
    await fetchData();
    // send some analytics events
  };

  return (
    <div className="container-div">
      {types.map((type) => {
        return (
          <button key={type} onClick={(e) => switchDocumentType(e, type)}>
            {type}
          </button>
        );
      })}
      <div id="special-div" dangerouslySetInnerHTML={{__html: documentData.terms}} />
    </div>
  );
};

export default MyComponent;
Advertisement