I'm new react user, when i see react-router docs, I confused.
let me show,
first, the docs url: https://reacttraining.com/react-router/web/example/route-config
I simplify like this
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
const routes = [
{
path: "/",
component: Sandwiches
},
{
path: "/tacos",
component: Tacos
}
];
export default function RouteConfigExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">/</Link>
</li>
<li>
<Link to="/tacos">tacos</Link>
</li>
</ul>
<Switch>
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} />
))}
</Switch>
</div>
</Router>
);
}
function RouteWithSubRoutes(route) {
return (
<Route
exact
path={route.path}
component={route.component}
/>
);
}
function Sandwiches() {
return <h2>/</h2>;
}
function Tacos() {
return (
<div>
<h2>Tacos</h2>
</div>
);
}
now, when i click / show /, but click Tacos show nothing.
what expect, click / show /, and click Tacos show Tacos.
I resolve by these
// first, do not use component wrap
export default function RouteConfigExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">/</Link>
</li>
<li>
<Link to="/tacos">tacos</Link>
</li>
</ul>
<Switch>
{routes.map((route, i) => (
<Route
key={i}
exact
path={route.path}
component={route.component}/>
))}
</Switch>
</div>
</Router>
);
}
// second, do not use Switch
export default function RouteConfigExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">/</Link>
</li>
<li>
<Link to="/tacos">tacos</Link>
</li>
</ul>
{routes.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} />
))}
</div>
</Router>
);
}
It bothered me for a long time, now I'm dying to know why, Please tell me in detail, and thanks so much...
In addition, My English is not pretty, understanding...
Switch components only valid children are Route and Redirect, so even though another react component renders a Route it won't work quite in the same way.
Switch children
All children of a <Switch> should be <Route> or <Redirect> elements.
Only the first child to match the current location will be rendered.
Edit
While wrapped Route components can still render, as "grand-children" descendants, it appears that route props are only applied at the root Switch level.
In the following Switch, even though RouteWithSubRoutes specifies the exact prop, RouteWithSubRoutes in the react DOM does not, so only the first element is returned (coincidentally the home "/" route).
<Switch>
{routes.map(route => (
<RouteWithSubRoutes key={route.path} {...route} />
))}
</Switch>
This following Switch is identical to the above except for specifying exact prop, and this works as expected.
<Switch>
{routes.map(route => (
<RouteWithSubRoutes key={route.path} exact {...route} />
))}
</Switch>
/Edit
It sounds like you have a two-part question, why both resolutions work.
The first attempt at fixing by rendering a Route directly succeeds by specifying the exact prop on all routes within the Switch, which only matches and renders the first matching Route or Redirect. When the path is exactly "/" then that component renders, and when it is exactly "/tacos" then that component renders.
The second attempt you render all the routes just right in the Router, which matches and renders all matching routes, but since you specify, again, the exact prop, it matches a single route and works.
Demo
Related
I am facing a very weird issue. In my react app, the navbar is something like:
<ul>
<li>
<a href={"/Marketplace"}>Marketplace</a>
</li>
</ul>
I have a simple login feature in my app which redirects the user to the homepage after logging in. After I log in, the href value in the above anchor tag disappears. I can't seem to understand why this is happening. basically, the same code block now becomes:
<ul>
<li>
<a href>Marketplace</a>
</li>
</ul>
If you want to allow users to link to other pages on click, Use Link tag !
for more help react-router-dom
import React from "react";
import { BrowserRouter, Link, Route, Switch } from "react-router-dom";
const Marketplace = () => {
return <div>This is the Market place</div>;
};
const Home = () => {
return <div>This is the home page</div>;
};
export default function App() {
return (
<div className="App">
<BrowserRouter>
<nav>
<div>
<Link to="/">Home</Link>
</div>
<div>
<Link to="/Marketplace">Marketplace</Link>
</div>
</nav>
<Switch>
<Route path="/Marketplace">
<Marketplace />
</Route>
<Route path="/" exact>
<Home />
</Route>
</Switch>
</BrowserRouter>
</div>
);
}
use Link instead of Anchor Tag if you are using react router
import { Link } from "react-router-dom";
<Link to="/Marketplace">Marketplace</Link>
I am hoping someone can confirm for me the expected behavior of react-router for nested routes. I would like to have base routes like 1) /route1 and then nested routes like 2) /route1/nested1 and 3) /route1/nested1/also. Is it possible for those nested routes to render child components without re-rendering that first route's component? Can someone confirm or possibly show a small code sample in which nested routes do not cause the parent component to re-render if you have done this successfully? Many thanks in advance. I believe this is not possible, and I astonished that react-router would be designed this way, since this is not how React itself works. I had thought perhaps the updates to the props.history caused the re-render, but if so, it does this whether or not those props are explicitly given to the component.
I have looked at the official docs and many pages of comments and questions online. Some folks seem say they were able to get this behavior while others say they simply cannot. Questions about this on the react-router GitHub were closed without being answered, since it was not considered a bug.
import React from 'react'
import { Link, Switch, Route, BrowserRouter as Router } from 'react-router-dom'
class Home extends React.Component {
render() {
return <div>Home</div>
}
}
class Route1 extends React.Component {
render() {
console.log('Route1 RENDER')
console.log('route1 props', this.props.history)
return (
<div>
<div>Route1</div>
<ul>
<li>
<Link to={'/route1/276364554643'}>nested route 1</Link>
</li>
<li>
<Link to={'/route1/276364554643/also'}>also</Link>
</li>
</ul>
<Route
exact
path={'/route1/:param_1'}
render={() => <NestedRoute1 />}
/>
<Route
exact
path='/route1/:param_1/also'
render={() => <Also />}
/>
</div>
)
}
}
class NestedRoute1 extends React.Component {
render() {
console.log('nested route history', this.props.history)
return <div>Nested Route Component</div>
}
}
class Also extends React.Component {
render() {
return <div>Also</div>
}
}
class Two extends React.Component {
render() {
return <div>Two</div>
}
}
class Three extends React.Component {
render() {
return <div>Three</div>
}
}
class App extends React.Component {
render() {
console.log('APP RENDER')
return (
<div className='App'>
<Router>
<div>
<ul>
<li>
<Link to='/'>home</Link>
</li>
<li>
<Link to='/route1'>route1</Link>
</li>
<li>
<Link to='/two'>two</Link>
</li>
<li>
<Link to='/any-other'>other</Link>
</li>
</ul>
<Switch>
<Route exact path='/' component={Home} />
<Route
path='/route1'
render={() => <Route1 />}
/>
<Route path='/two' component={Two} />
<Route path='/:other' component={Three} />
</Switch>
</div>
</Router>
</div>
)
}
}
export default App
EXPECTED: react-router should allow additions and changes to nested routes without an re-rendering parent components (the nested routes should match the component tree and behave the same way react behaves where changes in children do not trigger changes higher in the tree)
ACTUAL: changes to nested routes cause the parent component to re-render
If I create a react app, and in this app there's a link to a local html file, once this app published on gh-pages, for some reasons said link doesn't lead anywhere, it just redirect the user on the same page he was in before clicking the link .
For exemple:
If I create a simple app with CRA like so :
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
I'm the homepage
<a id='testlink' href='html2.html'> Go to the second page </a>
</div>
);
}
}
export default App;
And in the public folder I create a new html file "html2.html" that simply says
I am the second app !
And that's all, a simple app supposed to jump from index.html to html2.html when a link is clicked. Well this app works fine when tested with npm start, it works fine if launched via the static html file provided with npm run build, but when deployed on gh-pages, suddendly the link does not lead anywhere.
Here is the app described above deployed on ghpages
One workaround this problem would be to upload all the apps separately on gh-pages, or to use react router, but I wanted to know if I was just missing something. Thanks for your help.
On React projects you should use react-router to handle routes and change pages.
Simple example:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
const BasicExample = () => (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>
);
const Home = () => (
<div>
<h2>Home</h2>
</div>
);
const About = () => (
<div>
<h2>About</h2>
</div>
);
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${match.url}/rendering`}>Rendering with React</Link>
</li>
<li>
<Link to={`${match.url}/components`}>Components</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>Props v. State</Link>
</li>
</ul>
<Route path={`${match.url}/:topicId`} component={Topic} />
<Route
exact
path={match.url}
render={() => <h3>Please select a topic.</h3>}
/>
</div>
);
const Topic = ({ match }) => (
<div>
<h3>{match.params.topicId}</h3>
</div>
);
export default BasicExample;
I'm using react-router on a page with navbar and content.
const App = () => (
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/shape/:activeTab" component={Shapes} />
<Redirect from="/shape" to="/shape/triangle" />
</Switch>
</Router>
);
-- Shapes
class Shapes extends React.Component {
getActiveTab() {
switch (this.props.match.params.activeTab) {
case 'triangle':
return Triangle;
case 'circle':
return Circle;
case 'square':
return Square;
default:
return Triangle;
}
}
render() {
return (
<div className="wrapper">
// this is where the navbar renders.
<ul>
<li>
<NavLink exact to={`/shape/triangle`}> Triangle </NavLink>
</li>,
<li>
<NavLink exact to={`/shape/circle`}> Circle </NavLink>
</li>,
<li>
<NavLink exact to={`/shape/square`}> Square </NavLink>
</li>,
</ul>
// this is where the content renders.
<div className="content">
{
this.getActiveTab().data.map(tuple =>
<div>
{tuple.description}
</div>,
)
}
</div>
</div>
);
}
};
Now, when I click localhost:8080/shape I get redirected to proper page with navigation.
Also the navigation works properly (i:e. clicking on Triangle redirects me to localhost:8080/shape/triangle)
But, when I try to access the page localhost:8080/shape/triangle directly, I get a 404!
It works only when I access the /shapes page first and then use the navigation.
What do I need to address here to make sure I can route to shape/* pages.
Note: I'm referring https://reacttraining.com/react-router/web/api/Switch
Looking at this video the react router seems easy to use, but I can't find how to navigate in my code since I want to link on clicking a div and not use <Link>.
I've search StackOverflow but haven't found any answers that work with 4.0. Trying to import browserHistory gives undefined (before and after installing 'react-router' in addition to 'react-router-dom') from this question:
import { browserHistory } from 'react-router';
console.log('browserHistory:', browserHistory);
I also saw somewhere that there is a 'context' you can get to, but this shows a value for 'match' but not 'context':
<Route path="/" render={({ match, context}) => {
console.log('match:', match);
console.log('context:', context);
Edit
In the dev tools I can see that "Router" has a history property, so when I add that I can get to it:
<Route path="/" render={({ match, context, history}) => {
Is there a way to get to this from outside a route? For example a navbar component that will navigate to other components, but is not inside a Route itself...
If I understand your question, this is how you make a link programaticaly.
class Test extends React.Component {
handleClick() {
console.log(this.context);
this.context.router.history.push('/some/path');
},
render() {
return (
<div onClick={handleClick}>
This is div.
</div>
)
}
}
Test.contextTypes = {
router: React.PropTypes.object.isRequired
}
ReactDOM.render(
<Test />,
document.getElementById("app")
);
Had to read into the docs more. The history object is only passed as a property using the component (or other) attributes on a Route. Apparently need to include the 'history' package and use createBrowserHistory and pass it to the Router, then specify the component in a Route. I think this should work fine since exact isn't specified...
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
ReactDOM.render( (
<Router history={ history }>
<Route path="/" component={ App } />
</Router>
),
document.getElementById('root')
);
Before I just had <App/> inside <Router> and didn't have access to those properties.
Why don't you just wrap your div in the link instead of trying to circumvent it and make your life easier?
<Link to="/" >
<div className="to-component">go to component</div>
</Link>