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

Added Redirect with parameters #5209

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 15 additions & 3 deletions packages/react-router/docs/api/Redirect.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@ import { Route, Redirect } from 'react-router'

## to: string

The URL to redirect to.
The URL to redirect to. Any valid URL path that [`path-to-regexp`](https://www.npmjs.com/package/path-to-regexp) understands.
All URL parameters that are used in `to` must be covered by `from`.

```js
<Redirect to="/somewhere/else"/>
```

## to: object

A location to redirect to.
A location to redirect to. `pathname` can be any valid URL path that [`path-to-regexp`](https://www.npmjs.com/package/path-to-regexp) understands.

```js
<Redirect to={{
Expand All @@ -44,11 +45,22 @@ When `true`, redirecting will push a new entry onto the history instead of repla

## from: string

A pathname to redirect from. This can only be used to match a location when rendering a `<Redirect>` inside of a `<Switch>`. See [`<Switch children>`](./Switch.md#children-node) for more details.
A pathname to redirect from. Any valid URL path that [`path-to-regexp`](https://www.npmjs.com/package/path-to-regexp) understands.
All matched URL parameters are provided to the pattern in `to`. Must contain all parameters that are used in `to`. Additional parameters not used by `to` are ignored.

This can only be used to match a location when rendering a `<Redirect>` inside of a `<Switch>`. See [`<Switch children>`](./Switch.md#children-node) for more details.

```js
<Switch>
<Redirect from='/old-path' to='/new-path'/>
<Route path='/new-path' component={Place}/>
</Switch>
```

```js
// Redirect with matched parameters
<Switch>
<Redirect from='/users/:id' to='/users/profile/:id'/>
<Route path='/users/profile/:id' component={Profile}/>
</Switch>
```
19 changes: 18 additions & 1 deletion packages/react-router/modules/Redirect.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React from 'react'
import PropTypes from 'prop-types'
import generatePath from './generatePath'

/**
* The public API for updating the location programatically
* with a component.
*/
class Redirect extends React.Component {
static propTypes = {
computedMatch: PropTypes.object, // private, from <Switch>
push: PropTypes.bool,
from: PropTypes.string,
to: PropTypes.oneOfType([
Expand Down Expand Up @@ -43,9 +45,24 @@ class Redirect extends React.Component {
this.perform()
}

computeTo({ computedMatch, to }) {
if (computedMatch) {
if (typeof to === "string") {
return generatePath(to, computedMatch.params)
} else {
return {
...to,
pathname: generatePath(to.pathname, computedMatch.params)
}
}
}
return to
}

perform() {
const { history } = this.context.router
const { push, to } = this.props
const { push } = this.props
const to = this.computeTo(this.props)

if (push) {
history.push(to)
Expand Down
57 changes: 57 additions & 0 deletions packages/react-router/modules/__tests__/generatePath-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import expect from 'expect'
import generatePath from '../generatePath'

describe('generatePath', () => {
describe('with pattern="/"', () => {
it('returns correct url with no params', () => {
const pattern = '/'
const generated = generatePath(pattern)
expect(generated).toBe('/')
})

it('returns correct url with params', () => {
const pattern = '/'
const params = { foo: "tobi", bar: 123 }
const generated = generatePath(pattern, params)
expect(generated).toBe('/')
})
})

describe('with pattern="/:foo/somewhere/:bar"', () => {
it('throws with no params', () => {
const pattern = '/:foo/somewhere/:bar'
expect(() => {
generatePath(pattern)
}).toThrow()
})

it('throws with some params', () => {
const pattern = '/:foo/somewhere/:bar'
const params = { foo: "tobi", quux: 999 }
expect(() => {
generatePath(pattern, params)
}).toThrow()
})

it('returns correct url with params', () => {
const pattern = '/:foo/somewhere/:bar'
const params = { foo: "tobi", bar: 123 }
const generated = generatePath(pattern, params)
expect(generated).toBe('/tobi/somewhere/123')
})

it('returns correct url with additional params', () => {
const pattern = '/:foo/somewhere/:bar'
const params = { foo: "tobi", bar: 123, quux: 999 }
const generated = generatePath(pattern, params)
expect(generated).toBe('/tobi/somewhere/123')
})
})

describe('with no path', () => {
it('matches the root URL', () => {
const generated = generatePath()
expect(generated).toBe('/')
})
})
})
35 changes: 35 additions & 0 deletions packages/react-router/modules/generatePath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import pathToRegexp from 'path-to-regexp'

const patternCache = {}
const cacheLimit = 10000
let cacheCount = 0

const compileGenerator = (pattern) => {
const cacheKey = pattern
const cache = patternCache[cacheKey] || (patternCache[cacheKey] = {})

if (cache[pattern])
return cache[pattern]

const compiledGenerator = pathToRegexp.compile(pattern)

if (cacheCount < cacheLimit) {
cache[pattern] = compiledGenerator
cacheCount++
}

return compiledGenerator
}

/**
* Public API for generating a URL pathname from a pattern and parameters.
*/
const generatePath = (pattern = '/', params = {}) => {
if (pattern === '/') {
return pattern
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Obviously, this pattern does not contain any parameters. For an additional performance shortcut, we could assume that all strings that do not contain *, (, ) or : are eligible to pass through before creating and caching a compile function.

}
const generator = compileGenerator(pattern)
return generator(params)
}

export default generatePath