preactjs / preact-router

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

Allow URL change but block routing #455

Open foxt opened 1 year ago

foxt commented 1 year ago

Hello,

Is it possible to allow the URL to change, but leave the content of the router as is. For example, if I have the following code to show content outside of the router dependent on the path (think Twitter's old design, where tweets would open over top of the content you was looking at, but still changed the URL)

  <div>
    <Match path="/accountSettings">{({ matches }) => matches && <AccountDetailsModal />}</Match>
    <Router>
        <AsyncRoute path='/' .../>
        <AsyncRoute path='/foo' .../>
        <AsyncRoute path='/bar' .../>
    </Router>        
  </div>

Maybe something like onChange that's for example

canRoute={(e) => 
    if (e.url == '/accountSettings') return false;
    else return true;
}}

or:

<Router>
        <AsyncRoute path='/' .../>
        <AsyncRoute path='/foo' .../>
        <AsyncRoute path='/bar' .../>
        <FakeRoute path='/accountSettings' />
</Router>
foxt commented 1 year ago

You might be able to hack this in with

    /** Re-render children with a new URL to match against. */
    routeTo(url) {
        let matchingRoute = this._getMatchingChild(toChildArray(this.props.children), url)
        if (matchingRoute === undefined) return false;
        if (matchingRoute[0].props && matchingRoute[0].props.prevent) return true;

        this.setState({ url });

        // trigger a manual re-route if we're not in the middle of an update:
        if (!this._updating) this.forceUpdate();

        return true;
    },

ymmv

rschristian commented 1 year ago

What you want to do is route the new URL to the same component is all, and handle the changed router state from there. You can't skip the router without making the new URL unusable. For example,

import { Router } from 'preact-router';

function ExampleRoute({ optionalId }) {
  return (
    <div>
      {optionalId && <h1>Pretend I'm an overlay</h1>}
      <h1>Show overlay via link</h1>
      <a href="/12345">Click here</a>
    </div>
  );
}

export function App() {
  return (
    <div>
      <Router>
        <ExampleRoute path="/:optionalId?" />
      </Router>
    </div>
  );
}

Edit: Here's a (much prettier) StackBlitz example