Navigate in code with react-router-dom 4.0? - react-router

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>

Related

React (5): Make route navigate to an external link

I'm trying to make react navigate to an external link from a route. I don't feel like adding an restyling the header.
<Switch>
<Route exact path='/'>
<PageLayout>
<LandingPage />
</PageLayout>
</Route>
<Route exact path='/example'>
<a href="www.example.com" />
</Route>
</Switch>
I'm just looking for the simplest way to do this. I don't want to have to restyle the header.
Preferably it would open up a new page.
Edit I've also tried
<Route exact path='/example'>
<Redirect to='https://www.example.com' />
</Route>
react-router-dom only deals with internal routing & navigation within a React app. If you want are trying to navigate/redirect to a URL that is external to your app from a matched route then I suggest using window.open and open in a new browser context, like a new window or tab. You can create a custom component to do this as a mounting effect.
Example:
import { useHistory } from 'react-router-dom';
const RedirectExternal = ({ to }) => {
const history = useHistory();
React.useEffect(() => {
window.open(to, "_blank", "noreferrer");
// use timeout to move back navigation to end of event queue
setTimeout(history.goBack);
}, [history, to]);
return null;
};
Usage:
<Link to="/example">www.example.com</Link>
...
<Switch>
<RedirectExternal from="/example" to="https://www.example.com" />
<Route path="/">
<PageLayout>
<LandingPage />
</PageLayout>
</Route>
</Switch>
It might just be easier to link to the external page directly though.
<a href="https://www.example.com" rel="noreferrer" target="_blank">
www.example.com
</a>
Since you are using react-router-dom, you could do the following to achieve an external link in navigation.
<Route
path="/myPath"
component={() => {
if (window) {
window.open(
"https://www.google.com"
);
}
return null;
}}
/>

React-router-dom: Very simple nested routing does not work

I have searched through different tutorials and multiple stackOverflow questions. And none of which, helped me solve a very basic problem:
Implement nested routes with react-router-dom
Here's my code so far:
App.js
<Route exact path="/home" name="Home" component={DefaultLayout} />
DefaultLayout.js
<Route path="/home/users" component={Users} />
When I go to /home/users, I get a blank screen because react-router-dom is looking-up the definition of that route inside App.js instead of searching it inside DefaultLayout.js..
So I have two questions:
QUESION 1: What am I doing wrong exactly?
QUESTION 2: How does react-router-dom know that it should look for the nested route inside DefaultLayout.js instead of inside App.js?
It has been two days and I still cannot solve this simple problem.
Any help is very much appreciated.
EDIT 1: I have started a new project just for the sake of implementing a very simple nested routing:
App.js
import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import ParentComponent from "./nestedComponents/ParentComponent";
function App() {
return (
<div>
<BrowserRouter>
<Switch>
<Route exact path="/home" name="Home" component={ParentComponent} />
</Switch>
</BrowserRouter>
</div>
);
}
export default App;
ParentComponent.js
import React from "react";
import nestedComponentOne from "./nestedComponentOne";
import nestedComponentTwo from "./nestedComponentTwo";
import { Switch, Route } from "react-router-dom";
export default function ParentComponent() {
return (
<div>
PARENT COMPONENT
<Switch>
<Route path="home/nestedComponentOne" component={nestedComponentOne} />
<Route path="home/nestedComponentTwo" component={nestedComponentTwo} />
</Switch>
</div>
);
}
nestedComponentOne.js
import React from "react";
export default function nestedComponentOne() {
return <div>NESTED COMPONENT 1</div>;
}
nestedComponentTwo.js
import React from "react";
export default function nestedComponentTwo() {
return <div>NESTED COMPONENT 2</div>;
}
But, I still get a blank screen whenever I try to access a nested component...
You have this problem:
React-router urls don't work when refreshing or writing manually
The simplest fix is to replace the BrowserRouter with a HashRouter

React Router props `location` / `match` not updating with `ConnectedRouter`

I've got my app setup as in the docs:
Step 1
...
import { createBrowserHistory } from 'history'
import { applyMiddleware, compose, createStore } from 'redux'
import { connectRouter, routerMiddleware } from 'connected-react-router'
...
const history = createBrowserHistory()
const store = createStore(
connectRouter(history)(rootReducer), // new root reducer with router state
initialState,
compose(
applyMiddleware(
routerMiddleware(history), // for dispatching history actions
// ... other middlewares ...
),
),
)
Step 2
...
import { Provider } from 'react-redux'
import { Route, Switch } from 'react-router' // react-router v4
import { ConnectedRouter } from 'connected-react-router'
...
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}> { /* place ConnectedRouter under Provider */ }
<div> { /* your usual react-router v4 routing */ }
<Switch>
<Route exact path="/" render={() => (<div>Match</div>)} />
<Route render={() => (<div>Miss</div>)} />
</Switch>
</div>
</ConnectedRouter>
</Provider>,
document.getElementById('react-root')
)
I click on a Link or even dispatch(push('/new-url/withparam'))
However the props for match location are remaining the previous values or whatever the first page was.
What is happening?
This one has bitten me many times.
Your Switch and Route etc. MUST NOT BE INSIDE A CONNECTED COMPONENT!
If the component is connected, the props for match, location, etc. don't seem to get updated and propagate down to your routes.
This means don't connect your top level App or Root, or any other nested containers between the ConnectedRouter and Route
--
Update:
You may just need to wrap your component with
<Route render={ (routerProps) => <YourConnectedComponent { ...routerProps } />
I decided to add example to here as I feel it is valuable input - even tho, it's already answered.
I had similar problem, when I pushed url into router history, it changed URL but it didn't navigate properly on the component I wanted. I googled and searched for answer for hours, until I found this thread which finally helped me to find out what I made wrong. So all credits to #ilovett.
So here is an example, if someone will need it for better understanding:
I had code similar to this:
export const routes =
<Layout>
<Switch>
<Route exact path='/' component={ Component1 } />
<Route path='/parameter1/:parameterValue' component={ Component2 } />
</Switch>
</Layout>;
<Provider store={ store }>
<ConnectedRouter history={ history } children={ routes } />
</Provider>
It was working fine when I came to a project, but then I decided to refactor Layout component and I connected it to the store which caused that Component2 stopped receiving correct values in the ownProps.match.params.parameter1 and because of that it rendered component completely wrong.
So only thing what you need to do is move Layout outside of ConnectedRouter. Nothing between ConnectedRouter and Route can be connected to the store.
Working example is this then:
export const routes =
<Switch>
<Route exact path='/' component={ Component1 } />
<Route path='/parameter1/:parameterValue' component={ Component2 } />
</Switch>;
<Provider store={ store }>
<Layout>
<ConnectedRouter history={ history } children={ routes } />
</Layout>
</Provider>

react router v4 prevent to access to page

In React Router v4 doc I have read about Prompt to prevent to leave a page:
Prompt to prevent to leave, but until now I have not found anything some about prevent to access like willtransitionto in older version.
any suggestion?
Been researching this question a while ago, so, here are examples I came with (I am not sure if it's the best way, maybe there is some better soultions, but still i want to share):
1) When you preventing access and know to which page you must redirect user to:
Let say you have Home page and About page and you want to ask user confiramtion in case the user trying to visit it.
So, in this case we just can put this logic in render property in <Route> component, like this
render={(props)=>{
if(confirm('Are you sure you want to see this page?')){
return <About />
} else {
return <Redirect to='/'/>
}
}
}
So, if the user clicks OK it will show About page, otherwise it would redirect user to Homepage
class App extends React.Component{
render(){
return(
<Router>
<div className="container">
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Route exact path="/" component={Home} />
<Route path="/about" render={(props)=>{
if(confirm('Are you sure you want to see this page?')){
return <About />
} else {
return <Redirect to='/'/>
}
}
}/>
</div>
</Router>
)
}
}
Full example is here
2) Same as in 1st example but in case you want to redirect user back on the same page from which user trying to visit About page.
In this case, to be sure that App component gets location information I wrapped it up like this:
<Router>
<Route path="/" render={
(props)=>{
return <App {...props}/>
}
} />
</Router>
And then here, in the main appliction component (App) we can keep trace of the path, like this (so, everytime an App gets new properties regarding location and stuff from ReactRouter component, we can check it and save in our state):
constructor(props){
super(props);
this.state={
prevPath: props.location.pathname
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.location !== this.props.location) {
this.setState({ prevPath: this.props.location.pathname })
}
}
And then, in case of user wants to go to the about page we can, if user not confirmed the redirect, take him back to previous page, so, render property would look like this:
<Route path="/about" render={(props)=>{
if(confirm('Are you sure you want to see this page?')){
return <About />
} else {
let toPath = '/';
if(this.state.prevPath){
toPath=this.state.prevPath
}
return <Redirect to={toPath}/>
}
}
}/>
Full example here

Error refresh router react router

When I click on the link that contains url products/new I can access the new product page and it works fine, but if I refresh the page it returns Uncaught SyntaxError: Unexpected token <.
How can I resolve this?
Page product new
import React from 'react';
class ProductNew extends React.Component {
constructor(props) {
super(props);
}
render(){
return (
<div>
<div><h1>ProductNew</h1></div>
</div>
);
}
}
module.exports = ProductNew;
page products
import React from 'react';
import { Link } from 'react-router';
class Products extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div><h1>Products</h1></div>
<Link to="/products/new">new</Link>
</div>
);
}
}
module.exports = Products;
Page App
render((
<Router history={createBrowserHistory()}>
<Route path="/" component={Layout}>
<IndexRoute component={Home} />
<Route path="/products" component={Products}/>
<Route path="/products/new" component={ProductNew}/>
</Route>
</Router>
), document.getElementById('app'));
I had a similar problem, my App.js path were relative.
So when I loaded the app page from home path everything worked well, but when I tried to load from a link like yours "/product/new", I received an error like yours. When I put an absolute path, it worked fine.
I was using webpack to bundle the file and webpack-dev-server to run the development env and there is no physical files when you use webpack-dev-server hot loader (by default), it's a thing there is easy to fall in.
Edit:
this guy's question has 2 updates on it, with the same problem as you and has a better answer/info than that I gave to you.
whe you are using nested path for component Like http://127.0.0.1:8000/dashboard/myprojects then you should must add absolute path for your css and js in main index file..for example in laravel index.php.blade file add absolute path Like this: <'script src="{{ mix('js/app.js') }}">
</'script>