One simple solution would be to not use a traditional link. If you add a custom event listener you can just start a timeout with startTimeout(namedFunctionToSetLocation, 200ms)
. I strongly recommend being frugal and using flags to control the state complexity, which will otherwise try and eat you alive.
In more detail:
When someone triggers your event listener, firstly set some state which will add your classes "app-leave" and "app-leave-active". You can do this in a separate function but make sure to bind the function to your component's this
so it can access this.state
, or pass the state in when rendering.
In that function, you also want to start a timeout, so you can do the next you do of changes after a delay. It's worth generating an ID at this point, or setting a flag, so you can check after the delay that nothing else has begun in that time (if you use one ID variable for all transitions, and just update every time a new one begins, you should have a rock solid check whether, after an async delay, you still care about continuing).
When the timeout is triggered, and you've checked your flag, you can manually trigger your router, or set a new location, however you like.
I've found it useful to use a simple bespoke approach when ReactTransitionGroup isn't a perfect fit. The transition group has nuanced behaviours with ReactRouter v4, and it's completely unnecessary if all you need is a delay or simple class change on click.
Finally, you can also use the normal ReactRouter and ReactTransitionGroup components inside of conditional rendering blocks (like those I just described)- don't pass off that technique, it's as React-ee as it gets.