preactjs / preact-router

:earth_americas: URL router for Preact.
http://npm.im/preact-router
MIT License
1.02k stars 155 forks source link

When link is clicked, default browser event isn't prevented #340

Closed triallax closed 5 years ago

triallax commented 5 years ago

Explanation:

When clicking a link, the page correctly changes to the matching route. However, immediately after that, I get a new page saying Cannot GET /foo

This issue occurs only when using the a tag instead of Link.

pimdewit commented 5 years ago

This took me forever to debug, glad I'm not going crazy

triallax commented 5 years ago

I created this simple workaround for now. Put this in the component where you use the Router component:

useEffect(() => {
        function callback(event) {
            const target = event.target;
            if (
                target &&
                target.nodeName.toLowerCase() === 'a' &&
                !target.href.startsWith('mailto:')
            ) {
                if (event.stopImmediatePropagation) {
                    event.stopImmediatePropagation();
                }
                if (event.stopPropagation) {
                    event.stopPropagation();
                }
                event.preventDefault();
            }
        }
        window.addEventListener('click', callback);
        return () => {
            window.removeEventListener('click', callback);
        };
    }, []);

I didn't complicate it too much because my website isn't that complex anyways. Any suggestions are appreciated.

jschill commented 5 years ago

I have the same issue. routeTo() returns false, so the event is not prevented. https://github.com/preactjs/preact-router/blob/f7dbe6d07419aefb113a55d00af0436f7d9b8e15/src/index.js#L63

Called from here https://github.com/preactjs/preact-router/blob/f7dbe6d07419aefb113a55d00af0436f7d9b8e15/src/index.js#L119

developit commented 5 years ago

I just figured out what's causing this. In preact 10, Component.forceUpdate() is no longer synchronous. Unfortunately, preact-router is relying on synchronous updates in order to determine whether a clicked link was handled by any mounted routers. It invokes forceUpdate(), then immediately attempts to inspect a Boolean flag set by render():

https://github.com/preactjs/preact-router/blob/f7dbe6d07419aefb113a55d00af0436f7d9b8e15/src/index.js#L177-L178

developit commented 5 years ago

aaand we have a fix! https://github.com/preactjs/preact-router/pull/342