This is my first crack at trying to integrate React-Router4 into a React/Redux app. I've been stuck on the first hurdle for a couple hours as I'm reading the docs and a (very good) CSS-Tricks article.
The issue is that the won't render the 'Whatever' value I input at the component prop, which should be a component.
I've actually read almost entirely thru the (excellent) docs, beginning here: https://reacttraining.com/react-router/web/guides/quick-start
And I've consulted this article: https://css-tricks.com/react-router-4/
Which also includes this Codepen (to see if I did something obvious/stupid): https://codepen.io/bradwestfall/project/editor/XWNWge?preview_height=50&open_file=src/app.js
I'm just hoping someone on SO might be able to nudge me over this hump. I was expecting to at least be able to render this very simply without any additional libraries or adjustment to my stores based on: https://reacttraining.com/react-router/web/guides/redux-integration
entry point, index.js:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import store from './stores'
import { Provider } from 'react-redux'
import Home from './components/Home'
import TopStories from './components/TopStories'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
// app entry point
const app = (
<Provider store={store.configure(null)}>
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/topstories" component={TopStories} />
</Switch>
</Router>
</Provider>
)
// virtual DOM render
ReactDOM.render(app, document.getElementById('root'))
TopStories.js component (renders ok in the '/' path if I switch it w/ Home)
import React, { Component } from 'react'
export default (props) => {
return(
<div>Top Stories component </div>
)
}
I was attempting to test by typing the path into the browswer.... Duh. I just needed to set up a quick link component and everything worked fine.
Related
I've read some answers here and it seems they are all replying to a specific problem. Mine more about usage and placement rather than a problem I am facing. I also read the documentation but it does not answer my question.
My question is where to use react-router components?
Do I import (and use) BrowserRouter in index.js or App.js or does it go wherever the navigation bar is?
Do Route, Switch, NavLink, Link must be in the same module/file? If not, do I use them around the navigation bar element?
On the official website it shows that <Switch> is INCLUDED within <BrowserRouter> tags AFTER <Link> elements were used. Can I conclude from that <Switch> always come in the same element where <Link> elements were used?
I am beyond confused as to where these tags should be used in relation to other tags - not how to get them to work.
Finally, if I have a react app where index.js renders App.js. App.js is like a table of content of the main components I am rendering. Let's say: Header, Main, Footer. Inside the Header there are Greeting and then a NavigationBar. I want my links to be in the NavigationBar component as menu items. Where do I implement react-router components?
Thank you very much in advance.
1 . You can import and use BrowserRouter either in the root of the project which is index.js or in app.js just make sure it wraps around the Switch and Routes .
2 . The Switch Component must wrap all the Route components so this format is the most common way of using it :
<Switch>
<Route exact component={<Component />} path="/" />
</Switch>
You can use NavLink or Link almost anywhere in your components which are imported in Route component prop .
NavLink and Link is just for navigating between Routes in your app .
And for the last part you can have a Layout.js component to have Header Main and Footer components inside of it wrap all the other components which are used in Routes for example :
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// Router
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>
,
document.getElementById('root')
);
App.js
import React from "react";
// Router
import { Switch, Route, withRouter } from "react-router-dom";
// Components
import Home from "./routes/Home/Home";
function App() {
return (
<Switch>
<Route exact component={Home} path="/" />
</Switch>
);
}
export default App;
Home.js
import React from "react";
// Router
import { NavLink } from "react-router-dom";
// Components
import Layout from "../../components/Layout/Layout";
function Home(props) {
return (
<Layout title="Homepage">
<NavLink to="/ss">ss</NavLink>
<NavLink to="/">/</NavLink>
</Layout>
);
}
export default Home;
Define Header Footer and Main in Layout.js :
Layout.js
import React, { Fragment } from "react";
import { Helmet } from "react-helmet";
// Components
import Header from "../Header/Header";
import Footer from "../Footer/Footer";
function Layout({ title, children }) {
return (
<Fragment>
<Helmet>
<title>
{title}
</title>
</Helmet>
<Header />
{children}
<Footer />
</Fragment>
);
}
export default Layout;
The children prop is basically the Home.js component which has Layout wrapper so the Home.js component will display Header Footer from layout and it's own content .
This is the most common way of using the react-router-dom in a react project .
Note
The Switch component will only render the first route that matches/includes the path. Once it finds the first route that matches the path, it will not look for any other matches. Not only that, it allows for nested routes to work properly, which is something that Router will not be able to handle.
Fragment is just for wrapping the component tags like this :
import React , { Fragment } from 'react';
function Component() {
return (
<Fragment>
<Other />
<Main />
</Fragment>
);
}
If you delete Fragment it will throw an error you can read more about Fragments here .
i recently tried to make separate files .jsx in React. I made couple of them with export default name / import name from ./name.jsx'. But there comes problem. First i had imported Route and Links from react-router and console said it can't find Links, i found on stackoverflow to change it to react-router-dom, so i did it, and now console says i forgot to export some components. I can't find solution :( Could anyone help me ?
Here's my project :
https://github.com/tafarian/Portfolio
import React from 'react';
import ReactDOM from 'react-dom';
import './../css/style.scss';
import {
Router,
Route,
Link,
IndexLink,
IndexRoute,
hashHistory
} from 'react-router-dom';
import Template from './Template.jsx'
import Projects from './Projects.jsx'
import Home from './Home.jsx'
import AboutMe from './AboutMe.jsx'
import Contact from './Contact.jsx'
Such error as
"Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of App.
means that you are trying to create an undefined component, and as it said in error - usually this happens when you forget to export component or the component that you are importing does not exist.
When there is a lot of components and it's hard to find which one is "bad" I place a breakpoint at console.error(message); in React code and go up the stacktrace to the function createElement and it's arguments. Usually it helps me to identify the problem.
The main problem, is that your code is not compatible with react-router#4.
In this version you can not pass children and component to Route at the same time.
Also, there is no such a thing as IndexRoute and you should use BrowserRouter instead of Router, or you should pass an history object.
Take a look at the documention: https://reacttraining.com/react-router/web/guides/philosophy
And here is fixed version of yout app.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
import './../css/style.scss';
import {
BrowserRouter,
Route,
Link,
hashHistory
} from 'react-router-dom';
import Template from './Template.jsx'
import Projects from './Projects.jsx'
import Home from './Home.jsx'
import AboutMe from './AboutMe.jsx'
import Contact from './Contact.jsx'
document.addEventListener('DOMContentLoaded', function(){
class App extends React.Component {
state = {
loading: true,
};
componentDidMount() {
setTimeout(() =>
this.setState({
loading: false
}), 2500);
}
render() {
if(this.state.loading === true) {
return (
<div id="preloader">
<div id="loader"></div>
</div>
)
}
return(
<BrowserRouter history={hashHistory}>
<Template>
<Route exact path="/" component={Home} />
<Route path='/aboutme' component={AboutMe} />
<Route path='/projects' component={Projects} />
<Route path='/contact' component={Contact} />
</Template>
</BrowserRouter>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
});
I am using react-router-dom in a redux app.
This is my initial setup in index.js:
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
Then in my App.js I have:
render() {
return (
<div className="App">
<Route exact path="/" render={ () => {
return (
<div>
{
this.props.categories.map((category)=>{
console.log('category', category)
return (
<Link key={category.name} to="/category" >{category.name}</Link>
)
})
}
</div>
)
}}
/>
<Route path="/category" render={ () => {
console.log('category path this.props', this.props)
return (<p>Category is whatever</p>)
}}
/>
</div>
);
}
I would think that whenever I click any of the Links displayed the browser would automatically know how to render the new Route path /category but for some reason it does not.
What am I doing wrong?
The above post by Dane has the solution.
But in the spirit of presenting the solution with more clarity, I will copy and paste the relevant codes that made react router work well with redux and other middleware.
import { withRouter } from 'react-router-dom'
export default withRouter(connect(
mapStateToProps,
)(App))
From React Router docs,
Generally, React Router and Redux work just fine together.
Occasionally though, an app can have a component that doesn’t update
when the location changes (child routes or active nav links don’t
update). This happens if:
The component is connected to redux via
connect()(Comp).
The component is not a “route component”, meaning it
is not rendered like so: <Route component={SomeConnectedThing}/>
The
problem is that Redux implements shouldComponentUpdate and there’s no
indication that anything has changed if it isn’t receiving props from
the router. This is straightforward to fix. Find where you connect
your component and wrap it in withRouter.
So maybe it's a problem with using render props. So:
either replace render with component, or
try their solution, with withRouter ( even there you have to make them into components )
https://reacttraining.com/react-router/core/guides/redux-integration/blocked-updates
Both Link and Router is compulsory.
Not Work!
import { BrowserRouter as Link } from "react-router-dom";
Work in my case.
import { BrowserRouter as Router, Link } from "react-router-dom";
In my case, this is working properly. If you will import router and link both together.
import { BrowserRouter as Router, Link } from "react-router-dom";
I'm migrating to ReactRouter v4 and it's just been proving a hassle to me. I have a file in app.js in which i'm trying to setup a simple routing system.
import React, { Component } from 'react';
import { Router, Route, IndexRoute, hashHistory, Redirect } from 'react-router';
import LoginPage from './LoginPage';
import NavigationBar from './NavigationBar';
class App extends Component {
render() {
return (
<Router history={history}>
<Route exact path='/' component={NavigationBar}></Route>
<Route exact path='/login' component={LoginPage}></Route>
</Router>
);
}
}
The code compiles perfectly well, but for some reason on the browser, the navigation bar is shown instead of the login page when the url is localhost:3000/login
The image of the nagivation bar showing instead of the login
I intended the login to be shown at /login and the navigation bar to be shown at /, but instead the navigation bar is shown at both /login and /.
I've been following the examples on the react-router training guide here, and I can't seem to figure out what I'm doing wrong.
Maybe it's just a typo in your post here, but you're extending Compoent instead of Component. I would guess you'd see worse issues than getting the wrong component if that was happening in your code.
I prefer BrowserRouter (I don't have to support legacy browsers), but I got your code working like this:
import React, { Component } from 'react';
import { Route } from 'react-router';
import { HashRouter as Router } from 'react-router-dom';
import LoginPage from './LoginPage';
import NavigationBar from './NavigationBar';
class App extends Component {
render() {
return (
<Router>
<div>
<Route exact path='/' component={NavigationBar}></Route>
<Route exact path='/login' component={LoginPage}></Route>
</div>
</Router>
);
}
}
You'll have to install react-router-dom for this code to run.
Visiting http://localhost:3000/#/login, I get the Login component. If you change HashRouter to BrowserRouter, you can see your component render at http://localhost:3000/login.
I'm getting started with Walmart's react/redux/react-router/isomorphic boilerplate called electrode and I'm having trouble adding multiple routes. When I add the 2nd route it seems to do nothing and linking and pushing to the other routes does not change the page.
http://www.electrode.io/docs/get_started.html
https://github.com/electrode-io/electrode-redux-router-engine
Here's what the single route in the boilerplate looked like
// routes.jsx
import React from "react";
import {Route} from "react-router";
import Home from "./components/home";
export const routes = (
<Route path="/" component={Home}/>
);
and here's what I changed it to
import React from "react";
import {Route, IndexRoute} from "react-router";
import Home from "./components/home";
import Foo from "./components/foo";
export const routes = (
<Route path="/" component={Home}>
<Route path="/foo" component={Foo}/>
</Route>
);
I couldn't put the routes side by side because that gave me an error saying that jsx can't have two elements side-by-side so I had to nest it. The react router examples I see online seem to assume a root app component. Looking at the electrode router redux sample, they set the root component to "Page". What is the "Page" component? My questions are
Why doesn't my 2nd route work?
Should I be using an IndexRoute?
Do I need to create a root component for the root route? If so what does that component look like?
Here's the app.jsx code
//
// This is the client side entry point for the React app.
//
import React from "react";
import {render} from "react-dom";
import {routes} from "./routes";
import {Router} from "react-router";
import {createStore} from "redux";
import {Provider} from "react-redux";
import "./styles/base.css";
import rootReducer from "./reducers";
//
// Add the client app start up code to a function as window.webappStart.
// The webapp's full HTML will check and call it once the js-content
// DOM is created.
//
window.webappStart = () => {
const initialState = window.__PRELOADED_STATE__;
const store = createStore(rootReducer, initialState);
render(
<Provider store={store}>
<Router>{routes}</Router>
</Provider>,
document.querySelector(".js-content")
);
};
A few things...
You can avoid the "side by side jsx" warning by wrapping your routes in an empty route or returning an array.
// return nested routes
return (
<Route path="/">
<Route path="foo" component={Foo}/>
<Route path="bar" component={Bar}/>
</Route>
)
// return array, must use keys
return [
<Route key="foo" path="/foo" component={Foo}/>,
<Route key="bar" path="/bar" component={Bar}/>
]
If you want to nest routes, you need to give way to the child component by adding {this.props.children} to the parent component's render.
If you want truly separate routes that are not nested, it shouldn't be a child of the first route. I don't think adding an IndexRoute would provide any benefit unless you want some UI that is top level for all routes (like rendering a header, sidebar, etc).