Skip to content

Commit

Permalink
adds redirect with parameters + tests + docs
Browse files Browse the repository at this point in the history
  • Loading branch information
dlindenkreuz committed Jun 4, 2017
1 parent d196e91 commit ca77611
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 4 deletions.
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
}
const generator = compileGenerator(pattern)
return generator(params)
}

export default generatePath

0 comments on commit ca77611

Please sign in to comment.