Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

usePathMatch #9774

Closed
wants to merge 3 commits into from
Closed

usePathMatch #9774

wants to merge 3 commits into from

Conversation

Tobbe
Copy link
Member

@Tobbe Tobbe commented Dec 28, 2023

Finally have a solution to matching route paths

This PR and #9755 together provides a solution for the use case outlined in #7469

Here's the example from #7469 as it could be solved with the code in this PR

const Navbar () => {
  const { project } = useParams()
  const routePaths = useRoutePaths()

  const modes = [
    {
      name: "Info",
      route: routes.info({ project }),
      match: usePathMatch(routePaths.info), // using the new hook together with routePaths
    },
    {
      name: "Compare",
      route: routes.compare({ project, id: "1" }),
      match: usePathMatch(useRoutePath('compare')), // alternative to the above
    },
    // ...
  ]

  return (
    <>
      {modes.map((x) => <Button as={Link} to={x.route} isActive={x.match} />)}
    </>
  )
}

TODO:

  • More tests
  • Docs
  • Discuss with @jtoar
  • Better types
  • Make it possible to pass the same options as for useMatch

@Tobbe Tobbe marked this pull request as draft December 28, 2023 18:47
@Tobbe Tobbe force-pushed the tobbe-use-path-match branch from 53733bf to 1a0a30d Compare December 28, 2023 18:58
@Tobbe Tobbe added the release:feature This PR introduces a new feature label Dec 28, 2023
@Tobbe Tobbe added this to the next-release milestone Dec 28, 2023
import { useLocationMatch } from './useMatch'

interface UsePathMatchOptions {
paramValues?: Record<string, any>
Copy link
Member Author

@Tobbe Tobbe Jan 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

routeParams instead

Copy link
Contributor

@jtoar jtoar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paired with @Tobbe on this; some notes I added:

  • change useRoutePath() behavior when passed no args to output the current Route's path
  • see if we can fold this functionality into useMatch given a new routeParams option

@dennemark
Copy link
Contributor

Hi @Tobbe ,
I like the approach, after reading about the useRoutePath(s) hooks that are documented already in another commit.
While it is not as verbose as the other linked approch, where you can set dynamic and static parameters, this approch is much nicer to maintain!
Seems great!

@Tobbe
Copy link
Member Author

Tobbe commented Jan 3, 2024

@dennemark even with this solution (the new usePathMatch hook) you can set both static and dynamic parameters

Here's one of the examples from the comments on #7469

<Route path="/{dynamic}/{path}" page={ExamplePage} name="example" />

routes.example({ dynamic: 'dog', path: Param.ANY })
// => '/dog/{path}'

routes.example({ dynamic: Param.ANY, path: Param.ANY })
// => '/{dynamic}/{path}'

With this new hook you'd instead do this:

<Route path="/{dynamic}/{path}" page={ExamplePage} name="example" />

const exampleRoutePath = useRoutePath('example')
// => '/{dynamic}/{path}'

const matchOnlyDog = usePathMatch(exampleRoutePath, { routeParams: { dynamic: 'dog' }})
const matchFullyDynamic = usePathMatch(exampleRoutePath)

In the above example, if the current page url was https://example.org/dog/fido then both matchOnlyDog and matchFullyDynamic would have match: true.
If the current page instead was https://example.org/cat/garfield then only matchFullyDynamic would match

@Tobbe
Copy link
Member Author

Tobbe commented Jan 3, 2024

Closing this one in favor of #9793

@Tobbe Tobbe closed this Jan 3, 2024
jtoar pushed a commit that referenced this pull request Jan 3, 2024
Make it possible to specify route param values that need to match.

If this is your route: `<Route path="/blog/{year}/{month}/{day}"
page={BlogPostPage} name="blogPost" />`
And you want to only match posts from 2001 you can now do this:

`useMatch('/blog/{year}/{month}/{day}', { routeParams: { year: '2001' }
})`

This is **finally** a solution to matching route paths. The work started
in #7469, but we were never able to come up with an api/dx that we
really liked. This PR and #9755 together however provides a solution
that we're much more happy with, and that also supports the use case
outlined in that original PR.

Here's the example from #7469 as it could be solved with the code in
this PR

```jsx
const Navbar () => {
  const { project } = useParams()
  const routePaths = useRoutePaths()

  const modes = [
    {
      name: "Info",
      route: routes.info({ project }),
      match: useMatch(routePaths.info), // using the hook together with routePaths
    },
    {
      name: "Compare",
      route: routes.compare({ project, id: "1" }),
      match: useMatch(useRoutePath('compare')), // alternative to the above
    },
    // ...
  ]

  return (
    <>
      {modes.map((x) => <Button as={Link} to={x.route} isActive={x.match} />)}
    </>
  )
}
```

And, as described at the top of this description, we can also be more
specific than in that example if needed. Like if we only wanted to match
a specific project on the "Compare" route we could do this:

```jsx
  const modes = [
    {
      name: "Info",
      route: routes.info({ project }),
      match: useMatch(routePaths.info),
    },
    {
      name: "Compare against Alpha",
      route: routes.compare({ project, id: "1" }),
      match: useMatch(useRoutePath('compare'), { routeParams: { project: 'alpha' } }),
    },
    {
      name: "Compare against Beta",
      route: routes.compare({ project, id: "1" }),
      match: useMatch(useRoutePath('compare'), { routeParams: { project: 'beta' } }),
    },
    // ...
  ]
```

Here's another example

```jsx
<Route path="/{dynamic}/{path}" page={ExamplePage} name="example" />

const exampleRoutePath = useRoutePath('example')
// => '/{dynamic}/{path}'

const matchOnlyDog = useMatch(exampleRoutePath, { routeParams: { dynamic: 'dog' }})
const matchFullyDynamic = useMatch(exampleRoutePath)
```

In the above example, if the current page url was
`https://example.org/dog/fido` then both `matchOnlyDog` and
`matchFullyDynamic` would have `match: true`.
If the current page instead was `https://example.org/cat/garfield` then
only `matchFullyDynamic` would match

(This PR replaces #9774)
@jtoar jtoar removed this from the next-release milestone Jan 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release:feature This PR introduces a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants