Skip to content

Instantly share code, notes, and snippets.

@jedwards1211
Last active November 3, 2017 14:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jedwards1211/15140b65fbeafcbc14dec728fee16f59 to your computer and use it in GitHub Desktop.
Save jedwards1211/15140b65fbeafcbc14dec728fee16f59 to your computer and use it in GitHub Desktop.
Power <Match> for react-router v4

Usage

If any of notPattern(s) match or notExactPattern(s) match exactly, then it will not render the component. If any of the pattern(s) match or exactPattern(s) match exactly, or there are none, then it will render the component. If register={false} is given, it will not register any match on context, and child <Match> components will match on the same tail of the pathname (rather than the remainder after what was matched).

Example

const InAppShell = (): React.Element<any> => (
  <AppShell>
    <Match exactPattern="/" component={Home} />
    <Match pattern="/about" component={About} />
    <Match pattern="/contact" component={Contact} />
  </AppShell>
)

const App = (): React.Element<any> => (
  <div>
    <Match exactPattern="/" patterns={["/about", "/contact"]} register={false} component={InAppShell} />
    <Match notExactPattern="/" notPatterns={["/about", "/contact"]} register={false} component={NotFound} />
  </div>
)
import React, { PropTypes } from 'react'
import MatchProvider from 'react-router/MatchProvider'
import matchPattern from 'react-router/matchPattern'
import { LocationSubscriber } from 'react-router/Broadcasts'
class RegisterMatch extends React.Component<void, RegisterMatchProps, void> {
static contextTypes = {
match: PropTypes.object,
serverRouter: PropTypes.object
}
registerMatch() {
const { match:matchContext } = this.context
const { match } = this.props
if (match && matchContext) {
matchContext.addMatch(match)
}
}
componentWillMount() {
if (this.context.serverRouter) {
this.registerMatch()
}
}
componentDidMount() {
if (!this.context.serverRouter) {
this.registerMatch()
}
}
componentDidUpdate(prevProps) {
const { match } = this.context
if (match) {
if (prevProps.match && !this.props.match) {
match.removeMatch(prevProps.match)
} else if (!prevProps.match && this.props.match) {
match.addMatch(this.props.match)
}
}
}
componentWillUnmount() {
if (this.props.match) {
this.context.match.removeMatch(this.props.match)
}
}
render() {
return React.Children.only(this.props.children)
}
}
function getPatterns(pattern: ?string, patterns: ?Array<string>): Array<string> {
const result = []
if (pattern) result.push(pattern)
if (patterns) result.push(...patterns)
return result
}
class Match extends React.Component {
static defaultProps = {
exactly: false,
register: true,
}
static contextTypes = {
match: PropTypes.object
}
getMatch(location) {
const patterns = getPatterns(this.props.pattern, this.props.patterns)
const notPatterns = getPatterns(this.props.notPattern, this.props.notPatterns)
const exactPatterns = getPatterns(this.props.exactPattern, this.props.exactPatterns)
const notExactPatterns = getPatterns(this.props.notExactPattern, this.props.notExactPatterns)
const { match:matchContext } = this.context
const parent = matchContext && matchContext.parent
for (let notExactPattern of notExactPatterns) {
if (matchPattern(notExactPattern, location, true, parent)) return null
}
for (let notPattern of notPatterns) {
if (matchPattern(notPattern, location, false, parent)) return null
}
for (let exactPattern of exactPatterns) {
const match = matchPattern(exactPattern, location, true, parent)
if (match) return match
}
for (let pattern of patterns) {
const match = matchPattern(pattern, location, false, parent)
if (match) return match
}
if (!patterns.length && !exactPatterns.length) return {params: {}, exact: false, pathname: ""}
}
render() {
return (
<LocationSubscriber>
{(location) => {
const {
children,
render,
component:Component,
register,
} = this.props
const match = this.getMatch(location)
const props = { ...match, location }
const content = children
? children({matched: Boolean(match), ...props})
: match
? render
? render(props)
: <Component {...props} />
: null
if (register) {
return (
<RegisterMatch match={match}>
<MatchProvider match={match}>
{content}
</MatchProvider>
</RegisterMatch>
)
}
return content
}}
</LocationSubscriber>
)
}
}
export default Match
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment