简体   繁体   中英

Understanding click events and memory in react function components

I've been working with function components and hooks and i'm now trying to dig deeper into events and how they hold in memory. I've been using chrome dev tools performance tab to monitor the behavior. There is a few things I'm not clear on and maybe someone can clear this up for me.

So I did 3 different setups. First one to show obvious memory leak of events been added multiple times per render. which eventually causes a crash or infinite loop of renders. Or at least thats what looks like is happing.

const App = () => {
  const [count, setCount] = React.useState(0);
  const onKeyDown = () => setCount(count => count + 1);


  document.addEventListener('keydown', onKeyDown); 

    return (
    <div className='wrapper'>
      <div>Click any key to update counter</div>
      <div className='counter'>{count}</div>
    </div>
    );
};

ReactDOM.render(<App />, document.querySelector("#app"))

This shows an obvious spike in extra event calls per listener. See event log and then increase ladder of events been added.

在此处输入图片说明

Next up

const App = () => {
  const [count, setCount] = React.useState(0);
  const onKeyDown = () => setCount(count => count + 1);


 React.useEffect(() => {
   document.addEventListener('keydown', onKeyDown);
   return () => document.removeEventListener('keydown', onKeyDown);
  }, [] ); 

    return (
    <div className='wrapper'>
      <div>Click any key to update counter</div>
      <div className='counter'>{count}</div>
    </div>
    );
};

ReactDOM.render(<App />, document.querySelector("#app"))

The result was better in that the listener was only one call at a time. But I noticed the listener count was still going through the roof. The spike in when they got added wasn't as sharp. but the count of listeners was in the thousand. Where are all these listeners getting added. Is it listeners been added by jsfiddle. Probably best to isolate this test in just a html page outside jsfiddle.

在此处输入图片说明

Then I read about using the hook useCallback which memoizes the function and returns the cashed version of the function. So I tried this.

const App = () => {
  const [count, setCount] = React.useState(0);

    const cb = React.useCallback(() => {
      console.log('cb');
      setCount(count => count + 1);
    }, [] );

    return (
    <div className='wrapper'>
      <div onClick={cb}>Click any key to update counter</div>
      <div className='counter'>{count}</div>
    </div>
    );
};

ReactDOM.render(<App />, document.querySelector("#app"))

But this turned out to be similar to the last test using useEffect. Crazy amount of listeners still but no crashing like the first test.
So whats' the deal here am I missing something about memoizing using useCallback hook. Listeners look like they are been added like crazy and not been garbage collected.

I'm going to isolate this test without jsfiddle but just wanted to post to the community to get some insight on this first.

You don't use addEventListener in React!

Instead you'd do something like this:

const App = () => {
  let count = 0;
  const onAddHandler = () => {    
    count++;
    console.log(count);
    this._count.innerText = count;   
 }
 return (
    <div className='wrapper'>
       <div onClick={()=>onAddHandler()}>Click any key to update counter</div>
       <div className='counter' ref={(el) => this._count = el}></div>
       </div>
  );
}

Also, not sure why you're using React.useState. The whole point of functional components is that they're stateless. I'm not fond of this new useState hook to be used in a functional component.

The example you were probably looking for using hooks is:

import React, { useState, useEffect } from 'react';
import {render} from 'react-dom';

function Example() {
   const [count, setCount] = useState(0);

   useEffect(() => {
     document.title = `You clicked ${count} times`;
   });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
   </div>
 );
}

render(<Example />, document.getElementById('root'));

The React documentation https://reactjs.org/docs/hooks-effect.html says that

If you're familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

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