UNPKG

react-router

Version:

Declarative routing for React

141 lines (122 loc) 4.43 kB
import React from "react"; import { isValidElementType } from "react-is"; import PropTypes from "prop-types"; import invariant from "tiny-invariant"; import warning from "tiny-warning"; import RouterContext from "./RouterContext.js"; import matchPath from "./matchPath.js"; function isEmptyChildren(children) { return React.Children.count(children) === 0; } function evalChildrenDev(children, props, path) { const value = children(props); warning( value !== undefined, "You returned `undefined` from the `children` function of " + `<Route${path ? ` path="${path}"` : ""}>, but you ` + "should have returned a React element or `null`" ); return value || null; } /** * The public API for matching a single path and rendering. */ class Route extends React.Component { render() { return ( <RouterContext.Consumer> {context => { invariant(context, "You should not use <Route> outside a <Router>"); const location = this.props.location || context.location; const match = this.props.computedMatch ? this.props.computedMatch // <Switch> already computed the match for us : this.props.path ? matchPath(location.pathname, this.props) : context.match; const props = { ...context, location, match }; let { children, component, render } = this.props; // Preact uses an empty array as children by // default, so use null if that's the case. if (Array.isArray(children) && isEmptyChildren(children)) { children = null; } return ( <RouterContext.Provider value={props}> {props.match ? children ? typeof children === "function" ? __DEV__ ? evalChildrenDev(children, props, this.props.path) : children(props) : children : component ? React.createElement(component, props) : render ? render(props) : null : typeof children === "function" ? __DEV__ ? evalChildrenDev(children, props, this.props.path) : children(props) : null} </RouterContext.Provider> ); }} </RouterContext.Consumer> ); } } if (__DEV__) { Route.propTypes = { children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), component: (props, propName) => { if (props[propName] && !isValidElementType(props[propName])) { return new Error( `Invalid prop 'component' supplied to 'Route': the prop is not a valid React component` ); } }, exact: PropTypes.bool, location: PropTypes.object, path: PropTypes.oneOfType([ PropTypes.string, PropTypes.arrayOf(PropTypes.string) ]), render: PropTypes.func, sensitive: PropTypes.bool, strict: PropTypes.bool }; Route.prototype.componentDidMount = function() { warning( !( this.props.children && !isEmptyChildren(this.props.children) && this.props.component ), "You should not use <Route component> and <Route children> in the same route; <Route component> will be ignored" ); warning( !( this.props.children && !isEmptyChildren(this.props.children) && this.props.render ), "You should not use <Route render> and <Route children> in the same route; <Route render> will be ignored" ); warning( !(this.props.component && this.props.render), "You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored" ); }; Route.prototype.componentDidUpdate = function(prevProps) { warning( !(this.props.location && !prevProps.location), '<Route> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.' ); warning( !(!this.props.location && prevProps.location), '<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.' ); }; } export default Route;