简体   繁体   中英

React useState causing “Can't perform a React state update on an unmounted component..” error

I'm new to React/Node and working on a learning project. It's a platform that connects users (freelancers) with nonprofit companies. When a user logs in, they can go to the UserConnections page to view all the companies they connected with.

This is working correctly, but I also get the warning Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. Also when I visit this component, the command line starts spitting out connection messages extremely fast endlessly.

I think the problem is coming from this useEffect that is used in the UserConnections component. This block of code was a pain to write, and it took the help of multiple people on here to write it. I'm hesitant to touch it.

useEffect(() => {        
      const comps = connections.map((c) => VolunteerApi.getCurrentCompany(c));
      Promise.all(comps).then((comps => setCompanies(comps)));
    }    
  }, [connections]);  

I tried to rewrite it like so:

useEffect(() => {
    let isMounted = true;
    const comps = connections.map((c) => VolunteerApi.getCurrentCompany(c));
    if (isMounted) {
      Promise.all(comps).then((comps => setCompanies(comps)));
    }
    return () => { isMounted = false };        
  }, [connections]);

But that didn't change anything at all.

How can I change this code to avoid the memory leak without affecting the intended use of this function? Below is the code for this component. Thank you for any help.

UserConnections

function UserConnections() {
  const { currentUser, connectionHandles } = useContext(UserContext);
  const connections = currentUser.connections.concat(connectionHandles);
  const [companies, setCompanies] = useState([]);

  useEffect(() => {
    const comps = connections.map((c) => VolunteerApi.getCurrentCompany(c));
    Promise.all(comps).then((comps => setCompanies(comps)));
  }, [connections]);

  if (!companies || companies.length == 0) {
    return (
      <div>
        <p>You have no connections</p>
      </div>
    )
  } else {
    return (
      <div>
        {companies && companies.map(c => (
          <CompanyCard
            key={c.companyHandle}
            companyHandle={c.companyHandle}
            companyName={c.companyName}
            country={c.country}
            numEmployees={c.numEmployees}
            shortDescription={c.shortDescription}
          />
        ))}
      </div>
    );
  }
};

You got it almost right in your rewrite (great job!) - the isMounted check needs to go inside the then .

instead of:

    if (isMounted) {
      Promise.all(comps).then((comps => setCompanies(comps)));
    }

you write it like this:

      Promise.all(comps).then(comps => isMounted && setCompanies(comps));

It's also just a warning. You should fix it, yes, but it's not crazy critical.

EDIT:

The infinite loop problem:

const connections = currentUser.connections.concat(connectionHandles);

connections changes everytime you re-render your component (call setCompanies ) so the useEffect reference changes. The way to fix it is to use connectionHandles and currentUser as dependencies of the effect, and move connections inside of the effect like:

function UserConnections() {
  const { currentUser, connectionHandles } = useContext(UserContext);
  const [companies, setCompanies] = useState([]);

  useEffect(() => {
    let isMounted = true;
    // move connections inside the effect
    const connections = currentUser.connections.concat(connectionHandles);
    const comps = connections.map((c) => VolunteerApi.getCurrentCompany(c));
    Promise.all(comps).then((comps => isMounted && setCompanies(comps)));
    return () => { isMounted = false };
  }, [currentUser,connectionHandlers]);


...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM