diff --git a/packages/react-router/modules/Switch.js b/packages/react-router/modules/Switch.js index f1de5f7fc9..9d2e239b5b 100644 --- a/packages/react-router/modules/Switch.js +++ b/packages/react-router/modules/Switch.js @@ -42,29 +42,49 @@ class Switch extends React.Component { const { route } = this.context.router; const { children } = this.props; const location = this.props.location || route.location; + const { match, child } = this.getMatch(route, location, children); + + return match + ? React.cloneElement(child, { location, computedMatch: match }) + : null; + } + + getMatch(route, location, children) { + let child = null; + let match = null; - let match, child; React.Children.forEach(children, element => { if (match == null && React.isValidElement(element)) { - const { - path: pathProp, - exact, - strict, - sensitive, - from - } = element.props; - const path = pathProp || from; + if ( + React.Fragment != null && // Fragment support is only available in React.js >= 16.2 + element.type === React.Fragment + ) { + const subMatch = this.getMatch( + route, + location, + element.props.children + ); + child = subMatch.child; + match = subMatch.match; + } else { + const { + path: pathProp, + exact, + strict, + sensitive, + from + } = element.props; + const path = pathProp || from; - child = element; - match = path - ? matchPath(location.pathname, { path, exact, strict, sensitive }) - : route.match; + child = element; + match = path + ? matchPath(location.pathname, { path, exact, strict, sensitive }) + : route.match; + } } }); - return match - ? React.cloneElement(child, { location, computedMatch: match }) - : null; + return { child, match }; } } diff --git a/packages/react-router/modules/__tests__/Switch-test.js b/packages/react-router/modules/__tests__/Switch-test.js index 4c45ff466a..2d72459674 100644 --- a/packages/react-router/modules/__tests__/Switch-test.js +++ b/packages/react-router/modules/__tests__/Switch-test.js @@ -282,6 +282,124 @@ describe("A ", () => { expect(node.innerHTML).toMatch(/one/); }); + it("does handle fragments", () => { + const node = document.createElement("div"); + + ReactDOM.render( + + +

one

} /> + +

two

} /> +
+

three

} /> +
+
, + node + ); + + expect(node.innerHTML).toMatch(/two/); + }); + + it("does handle nested fragments", () => { + const node = document.createElement("div"); + + ReactDOM.render( + + +

one

} /> + +

two

} /> + + {null} +

three

} /> +
+
+

four

} /> +
+
, + node + ); + + expect(node.innerHTML).toMatch(/three/); + }); + + it("does not stop on nested fragments", () => { + const node = document.createElement("div"); + + ReactDOM.render( + + +

one

} /> + +

two

} /> +
+

three

} /> +
+
, + node + ); + + expect(node.innerHTML).toMatch(/three/); + }); + + it("does handle arrays", () => { + const node = document.createElement("div"); + + ReactDOM.render( + + +

one

} /> + {[

two

} />]} +

three

} /> +
+
, + node + ); + + expect(node.innerHTML).toMatch(/two/); + }); + + it("does handle nested arrays", () => { + const node = document.createElement("div"); + + ReactDOM.render( + + +

one

} /> + {[ +

two

} />, + [ + null, +

three

} /> + ] + ]} +

four

} /> +
+
, + node + ); + + expect(node.innerHTML).toMatch(/three/); + }); + + it("does not stop on nested arrays", () => { + const node = document.createElement("div"); + + ReactDOM.render( + + +

one

} /> + {[

two

} />]} +

three

} /> +
+
, + node + ); + + expect(node.innerHTML).toMatch(/three/); + }); + it("throws with no ", () => { const node = document.createElement("div");