React - how to trigger preventDefault() before the

2020-07-17 15:10发布

I have the following function within a React component which is run when a link is clicked:

onClick(e, { name }) {
    e.stopPropagation();

    const doIt = await checkSomething();

    if (doIt) {
        e.preventDefault();
        doSomethingElse();
    }
}

Problem is that by the time e.preventDefault() is reached the synthetic event is recycled. So, how do I tell the browser not to follow the link if it is clicked, when some logic is needed to calculate this decision and before the synthetic event is recycled?

4条回答
家丑人穷心不美
2楼-- · 2020-07-17 15:46

One way to handle this would be to use preventDefault and when the action is complete and you need to route, call the Routing logic yourself using history.push()

onClick(e, { name }) {
    e.stopPropagation();
    e.preventDefault();
    const doIt = await checkSomething();

    if (doIt) {
        doSomethingElse();
    } else {
        this.props.history.push('/pathToRoute');
    }
}

Check this answer on how to Programmatically navigate with React-router

查看更多
Evening l夕情丶
3楼-- · 2020-07-17 15:49

I'm just throwing an idea here.

If you can't or don't want to do the URL redirect programmatically, then there is an alternative.
You can nest a function (currying) and invoke the second function only after you finish your logic.
Note that this will require you to write your nested function as an IIFE.


Here is a small proof of concept using JavaScript (open console):

const el = document.getElementById("link");


function onLinkClick() {
  console.log('doing some stuff..');
  for(let i = 0; i < 500; i++){
  	console.log(`doing more stuff #${i}`);  
  }
  return function(e) {
    // this will trigger with the actuall event (note this is an IIFE)
    console.clear();
  }();
}

el.addEventListener('click', onLinkClick);
#big {
  min-height: 950px;
}
<div id="big">
  <a id="link" href="#test">Click to get to the bottom of the page</a>
</div>

<div id="test">Bottom of page</div>


Example with react:

class App extends React.Component {

  onClick = (e) => {
    console.log('doing some stuff..');
    for (let i = 0; i < 500; i++) {
      console.log(`doing more stuff #${i}`);
    }
    return ((e) => {
      //console.log(e, "inner function")
      console.clear();
    })(e)
  }

  render() {
    return (
      <div>
        <div style={{ height: '950px' }}>
          <a href="#test" onClick={this.onClick} >Click to get to the bottom of the page</a>
        </div>

        <div id="test">Bottom of page</div>
      </div>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

查看更多
戒情不戒烟
4楼-- · 2020-07-17 15:57

To use preventDefault on an event, you need to do it before the callback function returns. This is not something you can do after the completion of an asynchronous task.

If you look at MDN Event.preventDefault Notes, you'll notice it says you have to call it somewhere in the event flow.

Calling preventDefault() during any stage of event flow cancels the event, meaning that any default action normally taken by the implementation as a result of the event will not occur.

You can also refer to this SO question: event.preventDefault in async functions . In the top answer, you'll see mention of the use of await and how the event.preventDefault() needs to occur before the first await statement. This holds true for using promises, or any asynchronous action.

As an aside:

If you want to access the event properties in an asynchronous way, you should call event.persist() on the event, which will remove the synthetic event from the pool and allow references to the event to be retained by user code.

Here is the SyntheticEvent documentation.

查看更多
Emotional °昔
5楼-- · 2020-07-17 15:57

Have you tried using promises and .then():

// Async/Await
const asyncGreeting = async () => 'Greetings';

// Promises
const promiseGreeting = () => new Promise(((resolve) => {
  resolve('Greetings');
}));

asyncGreeting().then(result => console.log(result));
promiseGreeting().then(result => console.log(result));

You should be able to control preventDefault();

查看更多
登录 后发表回答