简体   繁体   中英

Using setTimeout inside React event, this synthetic event is reused for performance reasons

I wrote a React component like the following code

const RippleEffect = ({ children, controls }: any) => {
  const ctrl = (controls || ['button']) as string[];
  const effect = 'ripple';
  const newChildren = React.Children.map(children, element => {
    const onMouseDown = (e: any) => {
      // e.persist(); Does not work!

      const x = e.pageX - e.target.offsetLeft;
      const y = e.pageY - e.target.offsetTop;
      const w = e.target.offsetWidth.toString();

      const ripple = document.createElement('span');
      ripple.className = 'ripple';
      ripple.style.left = x + 'px';
      ripple.style.top = y + 'px';
      ripple.style.setProperty('--scale', w);
      e.target.appendChild(ripple);

      // Necessary but problematic!
      setTimeout(() => {
        e.target?.parentNode?.removeChild(ripple);
      }, 500);

    };
    if (ctrl.indexOf(element.type) != -1) {
      return React.cloneElement(element, { effect, onMouseDown })
    }
    else {
      return React.cloneElement(element)
    }
  });
  return newChildren;
}

It is possible to use the code in this way

 <RippleEffect>
        <button type="button" className="btn btn-primary">Primary</button>
        <button type="button" className="btn btn-secondary">Secondary</button>
        <button type="button" className="btn btn-success">Success</button>
 </RippleEffect>

Looks everything works but I got this error in the browser console:

Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property `target` on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist().

I think the problem happens when I am using setTimeout inside onmousedown event. I used e.persist() at the first line of onMouseDown function but it destroys the whole process!

NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.

Can anyone help me to fix this issue and remove the warning?

I solved the problem with

setTimeout(() => {
    ripple.remove();
}, 500);

instead of

setTimeout(() => {
    e.target?.parentNode?.removeChild(ripple);
}, 500);

The idea you have with e.persist() is right, when you call that function, React removes the synthetic event from the pool, and this allows references to it to be used asynchronously, which is what you are essentialy doing by calling setTimeout . You should persist the event. The reason you are getting NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. error is that you are appending ripple into the event.target , which is the button. But you are calling removeChild on the buttons parent node.

Uncomment e.persist() and change e.target?.parentNode?.removeChild(ripple); to e.target?.removeChild(ripple); .

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