Reputation: 26124
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?
Upvotes: 6
Views: 4738
Reputation: 959
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();
Upvotes: 0
Reputation: 31024
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.
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>
Upvotes: 2
Reputation: 282000
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
Upvotes: 2
Reputation: 5645
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.
Upvotes: 1