react-router-dom Switch needs additional div wrapper - javascript

I am using react, react-router-dom and redux to create a simple react application. The package.json file contains:
...
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
...
The project structure is as follow:
- src
- components
- Container
. index.js
+ Customers
+ Greetings
. App.js
- reducers
. customer.js
. index.js
. reducers.js
. Root.js
This is what index.js file looks like:
import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import Root from './Root'
import rootReducer from './reducers'
import registerServiceWorker from './registerServiceWorker'
const store = createStore(
rootReducer
)
render(<Root store={ store } />, document.getElementById('root'))
registerServiceWorker()
This is also what is my Root.js file looks like:
import React from 'react'
import PropTypes from 'prop-types'
import { Provider } from 'react-redux'
import { BrowserRouter as Router } from 'react-router-dom';
// Import app component
import App from './components/App'
const Root = ({ store }) => (
<Provider store={ store }>
<Router>
<App />
</Router>
</Provider>
)
Root.propTypes = {
store: PropTypes.object.isRequired
}
export default Root;
And following represents my App component:
import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom'
// import pages
import Container from './Container';
import Greetings from './Greetings';
import Customers from './Customers';
class App extends Component {
render() {
return (
<Switch>
<div> // <~~ This is where the issue happens
<Route path="/" component={ Container }></Route>
<Route exact path="/" component={ Greetings }></Route>
<Route path="/customers" component={ Customers }></Route>
</div>
</Switch>
);
}
}
export default App;
The problem is when I use <Switch /> as a wrapper of <Route /> tags, it needs an inner <div /> element, otherwise it will not work.
It means that if I remove the <div /> element inside the <Switch /> and wrap the <Route />s directly by <Switch /> the pages will not be rendered, and also no errors or exception throw in the console.
What is the problem? Am I doing wrong somewhere?
Any help is appreciated.
Edit:
Thanks all for useful comments. Here are some important things to consider:
I don't get any error or warning, react script works fine and compiles my code completely.
As you mentioned, only the first <Route> element of a specific path will be rendered, but when I add a <div> element as first level of <Switch> element as a wrapper for all <Route>s it will work fine. But the problem is I don't need the additional <div> element to be rendered. Please consider that I would like the Container component to be rendered in all pages and other components in same path ('/') should be rendered as children of Container.

There are two solutions:
The solution which Raghav mentioned in comments:
It could be done by making the first <Route /> -which I need to be rendered in all pages- as a container component and use it as a wrapper of other <Route /> inside the <Switch > tag. Gist link from #Raghav
<Switch>
<Container>
<Route exact path="/" component={ Greetings }></Route>
<Route path="/customers" component={ Customers }></Route>
</Container>
</Switch>
The second and a newer solution to this problem is using <React.Fragment> tag which is available in newer versions of React. <React.Fragment> will help you to wrap multiple elements in your component instead of using real html tags.
It will not render any extra elements into page
~~> Link to React documentation for Fragment

Related

Navigation issues: this.props history appears undefined

I am unable to go to another page because this.props.history appears undefined. Below is a screenshot of the error that I see. Note that I rendered my welcome component using this.props.history.push("/welcome") from the login component and it worked fine then.
This is my App.js
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Login from "./Components/Login";
import "./App.css";
import Signup from "./Components/Signup";
import Welcome from "./Components/Welcome";
import RecruiterPage from "./Components/Common/RecriterPage";
function App() {
return (
<Router>
<div className="App">
<Route exact path="/signup" component={Signup} />
<Route exact path="/" component={Login} />
<Route exact path="/welcome" component={Welcome} />
<Route exact path="/recruiter" component={RecruiterPage} />
</div>
</Router>
);
}
export default App;
My best guess is that the component you are trying to navigate from is a child component of a parent component that may or may not have access to history, hence has no access to the history props.
There are two ways to solving this:
1. Pass history as a props from the parent to the child component OR
2. Do this on the said component:
import { withRouter } from 'react-router-dom'
...and when exporting the component, wrap it in withRouter e.g.
export default withRouter(ComponentName)

Migrate 'react-router' into 'react-router-dom' (v4)

I am learning React Routing and I am watching this tutorial:
https://www.youtube.com/watch?v=1iAG6h9ff5s
Its 2016 tutorial so I suppose something changed because 'react-router' not working anymore and I am supposed to use 'react-router-dom'.
I found that I must uninstall 'history' and 'react-router' and use 'react-router-dom' instead, but It not working as expected when I change it.
How to edit this to make it working with 'react-router-dom'?
import React from "react";
import ReactDOM from "react-dom";
import {Router, Route, IndexRoute, hashHistory} from "react-router";
import Layout from "./pages/Layout";
import Archives from "./pages/Archives";
import Featured from "./pages/Featured";
import Settings from "./pages/Settings";
const app = document.getElementById('app');
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={Layout}>
<IndexRoute component={Featured}></IndexRoute>
<Route path="archives" component={Archives}></Route>
<Route path="settings" component={Settings}></Route>
</Route>
</Router>,
app);
My edit:
import React from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom";
import Layout from "./pages/Layout";
import Archives from "./pages/Archives";
import Featured from "./pages/Featured";
import Settings from "./pages/Settings";
const app = document.getElementById('app');
ReactDOM.render(
<Router>
<Route path="/" component={Layout}>
<Route path="/featured" component={Featured}/>
<Route path="/archives" component={Archives}/>
<Route path="/settings" component={Settings}/>
</Route>
</Router>,
app);
Also pushState not working...
Layout.js
import React from "react";
import {Link} from "react-router-dom";
export default class Layout extends React.Component {
navigate() {
this.props.history.pushState(null, "/");
}
render() {
return (
<div>
{this.props.children}
<h1>Welcome</h1>
<button onClick={this.navigate.bind(this)}>Featured</button>
</div>
);
}
}
When I click to Link url change, but content is not loaded... Also when I access url I get "Cannot GET" error
After watching the video, you probably want something like this. At first this would not be so easy to understand but after seeing a few of them you digest it. First you render your Layout with one Route. Then in this top route, you use other Routes to setup your components.
We usually use exact props for a top root like /. If you don't setup your app like that, for example all your routes is in your top Router config, then to use a route something like /featured we must have exact prop. If we don't use it Router always hit / path and we always see the top level component.
But, in your situation, you want other components to be routed in your top level component. So, we drop exact prop here.
Also you can use push to change history.
Update
After think about the navigation button named "Featured", I think you want the Featured component rendered as default one here. When hit the button again you will come back to Featured one. I've changed the code according to that. In this version, we add a / route in the Layout and point it to Featured. So, when we come here it is rendered. But, we use exact prop here since we also want routes like "/featured", "/archives" and "/settings".
export default class Layout extends React.Component {
navigate = () => this.props.history.push("/");
render() {
return (
<div>
<h1>Welcome</h1>
<Link to="/featured">Featured</Link>
<Link to="/archives">Archives</Link>
<Link to="/settings">Settings</Link>
<br />
<button onClick={this.navigate}>Featured</button>
<Route exact path="/" component={Featured} />
<Route path="/featured" component={Featured} />
<Route path="/archives" component={Archives} />
<Route path="/settings" component={Settings} />
<div>
Some other info.
</div>
</div>
);
}
}
const app = document.getElementById('root');
ReactDOM.render(
<Router>
<Switch>
<Route path="/" component={Layout} />
</Switch>
</Router>,
app);

electron + react router: initial app is not at root route

I have a setup in electron with react router. When I load the app into my browser, and don't go via electron, everything works fine, and the router is good.
When I use it in electron however, my software is not at the root route initially. I can navigate to the root route with the links I created, and that works fine. But it's not there initially, I get a 404. But I don't get any errors as such.
So when i start it, it looks like this:
But the Links all work, so I can click for example on Home at am redirected to the Home route:
My switch looks like this:
<Switch>
<Route exact path='/' component={Home}/>
<Route component={NotFound}/>
</Switch>
And that component sits in another component:
import React from 'react';
import Header from './Header.jsx'
import Main from './Main.jsx'
export default class App extends React.Component {
render() {
return (
<div style={{textAlign: 'center'}}>
<Header />
<Main />
</div>);
}
}
which sits in this component:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App.jsx';
import {BrowserRouter} from 'react-router-dom'
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'));
and here is also the Main.js which holds the Routes:
const Main = () => (
<main>
<Switch>
<Route exact path='/' component={Home}/>
<Route component={NotFound}/>
</Switch>
</main>
)
So as far as I am concerned, everything should be working fine, and, as I said, it does in the browser. But electron does not seem to start my react router at /. Why could this be?

Pagination issue using React Router v4.1

I'm migrating a site in ASP.NET MVC to REACT. And for pagination i have created a component in React.
Issue i'm facing is with Routing for the pagination URLs. React Router is not able to detect that the URL is different when i click on a pagination URL
Let me explain:
app.js code:
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware} from 'redux';
import allReducers from '../reducers/index';
import {Provider} from 'react-redux';
import ReduxPromiseMiddleware from 'redux-promise';
import { BrowserRouter, Route } from 'react-router-dom';
import Main from './main';
import Layout from './layout';
const app = document.getElementById('root');
const store = createStore(allReducers, applyMiddleware(ReduxPromiseMiddleware));
ReactDOM.render(<Provider store={store}>
<BrowserRouter>
<Layout>
<Main/>
</Layout>
</BrowserRouter>
</Provider>
,app);
Main component render:
render(){
return(
<main>
<Switch>
<Route exact path='/' component={HomePage}/>
<Route path='/posts' component={PostsRouter} />
<Route path='/studies' component={StudiesPage} />
</Switch>
</main>
);
}
PostsRouter component:
const PostsRouter = () => (
<Switch>
<Route exact path='/posts' component={PostsPage} />
<Route path='/posts/:page' component={PostsPage} />
</Switch>
);
For both /posts and /posts/2 i need the component to be PostsPage.
Lets say i'm at /home. Now i click a posts link and URL changes to /posts. Now if i click /posts/2 link, nothing happens. React Router doesn't detect that the URL is different.
And a weird thing i noted is that if i change the component:
<Route path='/posts/:page' component={PostsPage} />
to
<Route path='/posts/:page' component={StudiesPage} />
then React Router routes me to StudiesPage component if i click on /posts/2 link when i'm on /posts URL.
May be i'm missing something obvious. But i haven't been able to figure out a way after lots of attempts.
I suspect Sergey's comment was right, that's what my problem ended up being. I was fetching data within componentDidMount() but didn't realise that in order to actually update it with new data when the next page link was clicked, I needed to do the same thing inside componentWillReceiveProps(). You can see my full source here but the biggest key part was this:
componentWillReceiveProps(nextProps) {
this.setState({
loaded: false
});
this.fetchMediaItems(nextProps.match.params.page);
}
componentDidMount() {
this.fetchMediaItems(this.props.match.params.page);
}
componentWillReceiveProps() receives the new properties, including page number, when you click on the link to page 2, so you need to do whatever inside there to update with the new state.

React router not working

I have a simple react-router setup. Please see my code here -
https://github.com/rocky-jaiswal/lehrer-node/tree/master/frontend
It is the most basic setup for react-router, however in the browser I cannot get it working -
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route } from 'react-router';
import IndexContainer from './components/index-container';
import AboutContainer from './components/about-container';
import PagesContainer from './components/pages-container';
(function main() {
ReactDOM.render((
<Router>
<Route path="/" component={IndexContainer}>
<Route path="about" component={AboutContainer} />
<Route path="pages" component={PagesContainer} />
</Route>
</Router>),
document.getElementById('app')
);
})();
The trouble also is there is no error reported on the console. Even I change the URL the IndexComponent is always mounted. Also if I type http://localhost:3333/#/about it changes to http://localhost:3333/#/about?_k=bac2pt somehow and stays on the IndexComponent.
Could there be something wrong with my simple webpack config or versions of react / react-router?
Thanks,
Rocky
IndexContainer is your parent component, try adding { this.props.children } in your render() so AboutContainer and PagesContainer will show.

Categories

Resources