React-router link doesn't work - reactjs

React-router is off to a really bad start... What seems basic doesn't work. Using react-router 2.0.0 my Link component updates the URL to be /about, but my page doesn't render the About component after that...
Entry point js
var React = require('react');
var ReactDOM = require('react-dom');
var Router = require('react-router').Router;
var Route = require('react-router').Route;
var hashHistory = require('react-router').hashHistory;
var App = require('./components/App.react');
var About = require('./components/About');
ReactDOM.render(
<Router history={hashHistory} >
<Route path="/" component={App}>
<Route path="about" component={About} />
</Route>
</Router>,
document.getElementById('app')
);
App.js
'use strict';
var React = require('react');
var Link = require('react-router').Link;
var Header = require('./Header');
var UserPanel = require('./UserPanel');
var ModelPanel = require('./ModelPanel.react');
var EventPanel = require('./event/EventPanel');
var VisPanel = require('./vis/VisPanel');
var LoginForm = require('./LoginForm');
var AppStore = require('../stores/AppStore');
var AppStates = require('../constants/AppStates');
var App = React.createClass({
[... code omitted ...]
render: function() {
var viewStateUi = getViewStateUi(this.state.appState);
return (
<div>
<Header />
<Link to="/about">About</Link>
{viewStateUi}
</div>
);
}
});

For some reason, the <Link>s were not working for me with the configuration below.
// index.js
ReactDOM.render(
<Provider store={store}>
<BrowserRouter >
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
// App.js
return (
<div className="App">
<Route exact={true} path="/:lang" component={Home} />
<Route exact={true} path="/" render={() => <Redirect to={{ pathname: 'pt' }} />} />
<Route path="/:lang/play" component={Play} />} />
<Route path="/:lang/end" component={End} />
</div >
);
The Home component had the Link, but Links on the App would do the same. Every time I clicked it, it would only change the url, but the views would stay the same.
I was able to put it working when I added withRouter to the App.js
export default withRouter(connect(mapStateToProps, { f, g })(App));
I still don't understand what happened. Maybe it's related with redux or there is some detail I'm missing.

Since the 'About' route is a child of the 'App' route, you need to either add this.props.children to your App component:
var App = React.createClass({
render: function() {
var viewStateUi = getViewStateUi(this.state.appState);
return (
<div>
<Header />
<Link href="/about">About</Link>
{viewStateUi}
{this.props.children}
</div>
);
}
});
or separate your routes:
ReactDOM.render(
<Router history={hashHistory} >
<Route path="/" component={App} />
<Route path="/about" component={About} />
</Router>,
document.getElementById('app')
);

None of the solutions worked for me, including adding withRouter to my Component. I was experiencing the same issue where the browser's address bar updates the URL but the component doesn't render. During the debugging of my issue, I realize I have to present the context of my problem because it is a bit different from what the OP had.
The route I was trying to get to work was a dynamic route that takes an arbitrary parameter, e.g.
<Route path={`/hr/employees/:id`} component={EmployeePage} />
The component this route uses is "self-referential", meaning that within the component or its children components, they have a Link component that directs to /hr/employees/:id, but with a different id. So let's say if I was at /hr/employees/3 and on the page, there was a link to /hr/employees/4, e.g. <Link to='/hr/employees/4'>, I would get this problem where the component didn't re-render.
To solve this problem, I simply modified the componentDidUpdate method of my EmployeePage component:
componentDidUpdate(prevProps) {
if (this.props.match.params.id !== prevProps.match.params.id) {
// fetch data
}
}
If you're using functional components, use useEffect:
const EmployeePage = props => {
const {id} = props.match.params
useEffect(() => {
// fetch data
}, [id])
}

Related

React Router v4 not navigating to historical pages using back/forward buttons

I'm using react-router-dom: v4.1.1, and this is my router code:
var ReactRouter = require('react-router-dom');
var Router = ReactRouter.BrowserRouter;
var Route = ReactRouter.Route;
var Switch = ReactRouter.Switch;
var browserHistory = ReactRouter.BrowserHistory;
<Router history={browserHistory}>
<div className='container'>
<Switch>
<Route exact path='/' render={(props) => <Home
{...props} // for router history and all that.
rebase={rebase}
items={this.state.items}
user={this.state.currentUser}
userData={this.state.currentUserData}/>} />
<Route path='/browse' render={() => <Browse items={_.values(this.state.items)}/>} />
<Route render={function () {
return <p>Not Found</p>
}} />
</Switch>
</div>
</Router>
In the Home component I have a button which, when clicked, adds a new url to the history: this.props.history.push('/?p=' + hash) where hash is simply something like f9ojy. This 'works' in the sense that the URL in the window is updated to the new hash, but when I click 'back' or 'forward' there's no change in the displayed page.
Found the answer in the Reactiflux discord channel; in componentDidUpdate I can check the previous this.props.location.search and compare it against the current one. If it's not the same I have a function which resets the UI based on the current parameter.

How to update state after React Router changes automatically?

I have a file with router and a component. Shortly, the code is like this:
// define the routes for each language ..
const InnerRoutes = (
<Route>
<IndexRoute page="home" component={StaticPage}></IndexRoute>
<Route path="contacts" component={ContactsPage}></Route>
<Route path="(:page)" component={StaticPage}></Route>
</Route>
);
// define the routes for all the languages, using InnerRoutes ..
const AllRoutes = (
<Router history={browserHistory}>
<Route path='/' component={App} language="bg">
{InnerRoutes}
<Route path="en" language="en">
{InnerRoutes}
</Route>
</Route>
</Router>
);
// and render our app ..
ReactDOM.render(
AllRoutes,
document.getElementById('app')
);
My question is: how can I have the App component state changed when router change is triggered?
And of course - have the router params in the app state.
(Because currently I can take the router stuff from the App component's method componentDidUpdate and then trigger setState to change the App state. Unfortunately - then I have the componentDidUpdate triggered twice.)
I've added this to my App and it seems to receive changes when routes change. More reference here
class App extends React.Component {
...
getInitialState() {
return {
lang: 'en' // default
};
}
componentWillReceiveProps(props) {
console.log('location: ', props.location.pathname);
var newLang = props.location.pathname.split('/').shift();
if(this.state.lang !== newLang) {
this.setState({lang: newLang});
}
}
render() {
const lang = this.state.lang;
return (
<AboutPage language={lang} />
<Support language={lang} />
);
}
}
If this doesn't work, you can also look into how two components talk to each other

Navigation from component to another componet

I have login component and dashboard. My application entry point is login page. After successful login, I want move to main page(dashboard & navigation).
I tried this like the following but its not working. After login not able to move to dashboard.
My login Component is
var React = require('react');
var {Link} = require('react-router');
var Dashboard = require('Dashboard');
var Login = React.createClass ({
onFormSubmit: function(e){
e.preventDefault();
<Dashboard />
},
render: function(){
return (
<div>
<h1 className="text-center">Login</h1>
<form onSubmit={this.onFormSubmit}>
<input type="text" ref="username"/>
<button className="button">Login</button>
</form>
</div>
)
}
});
module.exports = Login;
My Dashboard component is
var React = require('react');
var Nav = require('Nav');
var Dashboard = (props) => {
return (
<div>
<Nav/>
<div className="row">
<div className="columns medium-6 large-4 small-centered">
{props.children}
</div>
</div>
</div>
);
}
module.exports = Dashboard;
Main app.jsx file is
var React = require('react');
var ReactDOM = require('react-dom');
var {Route, Router, IndexRoute, hashHistory} = require('react-router');
var Login = require('Login');
var Dashboard = require('Dashboard');
var About = require('About');
var Examples = require('Examples');
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={Login}>
<Route component={Dashboard}>
<Route path="about" component={About}/>
<Route path="examples" component={Examples}/>
</Route>
</Route>
</Router>,
document.getElementById('app')
);
Your onFormSumit() should be like this
onFormSubmit: function(e){
e.preventDefault();
window.location.href = '/dashboard'
},
And your <Router/> in app.jsx should be like this
<Router history={hashHistory}>
<Route path="/" component={Login} />
<Route path="dashboard" component={Dashboard} />
<Route path="about" component={About}/>
<Route path="examples" component={Examples}/>
</Router>
This will make the browser redirect to /dashboard on form submit. That redirected url(/dashboard) will be captured by the <Router/> and the component for that path will be rendered.
[Update]
In your express, you nee to add the path so that it always returns the index.html page. Add this
app.get(['/', '/dashboard'], function(req, res){
res.sendfile('./path to your index.html file')
})
or you can do the following also
app.get('*', function(req, res){
res.sendfile('./path to your index.html file')
})
For further read, check this

How to nest React Routes in homepage

I'm trying to figure out how to properly nest routes inside the homepage. Here's my Router:
var appRouter = (
<Router history={ hashHistory }>
<Route path="/" component={ Navbar }>
<IndexRoute component={ Homepage }/>
<Route path="items" component={ Homepage }>
<Route path=":item" component={ ItemShow }/>
</Route>
<Route path="nothome" component={ NotHome }/>
</Route>
</Router>
)
Having an IndexRoute and Route that both point to Homepage doesn't seem optimal, but it gives me the behavior I'm looking for. Here's my whole project (I wrote this just to illustrate this point).
//React
var React = require("react");
var ReactDOM = require("react-dom");
//Router
var ReactRouter = require('react-router')
var Router = ReactRouter.Router
var Route = ReactRouter.Route
var IndexRoute = ReactRouter.IndexRoute
var hashHistory = ReactRouter.hashHistory
var Link = ReactRouter.Link
var items = [1, 2]
var Navbar = React.createClass({
render(){
return(
<div>
<Link to="/"><h1>Navbar</h1></Link>
{this.props.children}
</div>
)
}
})
var Homepage = React.createClass({
render(){
return(
<div>
<h2>This is the homepage</h2>
<ItemList/>
<Link to="/nothome">Another page</Link>
{this.props.children}
</div>
)
}
})
var ItemList = React.createClass({
render(){
return(
<ul>
{items.map( item => {
return <Item key={item} id={item}></Item>
})}
</ul>
)
}
})
var Item = React.createClass({
handleClick(){
hashHistory.push("items/" + this.props.id)
},
render(){
return(
<li onClick={this.handleClick}>Item {this.props.id}</li>
)
}
})
var ItemShow = React.createClass({
render(){
return(
<div>
You clicked on item {this.props.params.item}
</div>
)
}
})
var NotHome = React.createClass({
render(){
return(
<h2>This is not the homepage</h2>
)
}
})
var appRouter = (
<Router history={ hashHistory }>
<Route path="/" component={ Navbar }>
<IndexRoute component={ Homepage }/>
<Route path="items" component={ Homepage }>
<Route path=":item" component={ ItemShow }/>
</Route>
<Route path="nothome" component={ NotHome }/>
</Route>
</Router>
)
document.addEventListener("DOMContentLoaded", ()=>{
ReactDOM.render(appRouter, document.getElementById("root"))
})
Another option would be to put a Homepage component at the top of my ItemShow component and not nest the routes, but that seems just as bad if not worse.
It seems like there must be a preferable way to get this behavior. What is it?
Looking for your code, it seems that you don't really need to have this "items" route, since both "/" and "/items" render the same component (<Homepage>).
So, if you want to avoid having two "Homepage" declaration, you can redirect your user to "items", whenever they go to "/". You can do that by using <IndexRedirect> or <Redirect> or onEnter hook.
More information about the hook:
https://github.com/reactjs/react-router/blob/v2.5.2/docs/guides/IndexRoutes.md#index-redirects
If you really want to be able to access those two routes pointing to the same component, maybe you don't have to change anything. But, in your case, I would have a "Homepage" (even with some dummy info) and a "Items Homepage", and it would avoid the "Homepage" repetition.
BTW, just as a tip, I would rename your <NavBar> to "<App>" or something like that, since it would be better to understand your code!

React router page doesn't reload

I have a problem with react-router. The page content doesn't update when I click on a link (I'm using Link from react-router), although the URL is updated. Below is the source code:
React.render(
<Router>
<Route path="/" component={App}>
<IndexRoute component={Dashboard} />
<Route path="phase/:phaseNo" component={PhaseDetail}/>
</Route>
</Router>, document.getElementById("glyco-edu")
);
PhaseDetail.js:
var React = require("react");
var PhaseDetail = React.createClass({
render: function () {
return( <div>
Example Text
</div>
);
}
});
module.exports = PhaseDetail;
Link element:
<Link to={'/phase/'+this.props.phaseNo}>
{images}
</Link>
I'm using react-router-1.0.0-rc3 and react-0.13.3.
Thank you.

Resources