Hello guys and welcome to the next episode of New to React so lets experiment! In this episode we present:
Trying display the Home component on application start.
I went with building the homepage in a different component, not sure if this is the right approach but I thought it would be nice to keep the App.js clean from all the html and such.
Doing this though, causes a problem it seems, as it does not seem to show the Home component on app boot (duh), instead it just displays a black page. In order to get to the hompage you need to click the navbar brand link in the Navigation since I've added a link for that.
class App extends Component {
render() {
return (
<BrowserRouter>
<div className="App">
<Navigation />
<div>
<div>
<h1>Some content</h1>
</div>
<div>
<Route exact path="/" component={Home} />
<Route path="/page1" component={page1} />
<Route path="/page2" component={page2} />
</div>
</div>
<Footer />
</div>
</BrowserRouter>
)
}
}
I think I could use a <Redirect> to redirect to the Home component but this seems like a workaround to me, rather than what should actually happen.
What would be a better way of doing this?
Related
So basically, I have a problem with react router not rendering my SystemSidebar. I want to scroll through my SystemSidebar components, but my problem is when I press on 'LinkSoundIcon' it redirects me to 'a new page' but that page doesnt render my systemSidebar . I want when I press on any of the links of my sidebar that my sidebar remains
import React from 'react'
import './SystemSidebar.css'
import SoundIcon from '#material-ui/icons/Computer';
import ComputerIcon from '#material-ui/icons/Computer';
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';
import Sound from './Sound';
import Computer from './Computer;
const SystemSidebar=()=> {
return (
<div className='system'>
<div className="sidebar">
<Link to='Sound'><VolumeUpIcon /></Link>
<h4> Sound</h4>
<Link to='Computer'><ComputerIcon /></Link>
<h4> Computer</h4>
</div>
</div>
);
};
import React,{Component} from 'react'
import Sound from './Sound';
import Computer from './Computer';
import SystemSidebar from './SystemSidebar';
class MainSystem extends Component {
render(){
return (
<div className="MAIN">
<BrowserRouter>
<SystemSidebar />
<Switch>
<Route exact path="/" component={SystemSidebar} />
<Route exact path="/Sound" component={Sound}/>
<Route exact path="/Computer" component={Computer}/>
</Switch>
</BrowserRouter>
</div>
);
}
}
export default MainSystem;
<Link to='/Sound'><VolumeUpIcon /></Link>
answer of your first problem and second if you want to access sidebar in each component then don't put it in switch route , simply put it outside the routing... or if u want to access it with specific route then try using nested routing
Okay, so it seems a little wonky with your copy pasting (I hope this is just a problem that stems from copy and pasting and it's not like that in your code). But your Problem is here:
<Route exact path="/Sound" component={Sound}/>
You're saying here that the route should be EXACTLY http://<your root uri>/Sound
You should also use this exact route in the link if you want to hit it, this means you need to have the slash there:
<Link to='/Sound'><VolumeUpIcon /></Link>
Update:
So according to your comment you want the sidebar to stay when you click a link. In this case, take a look at your code:
<Switch>
<Route exact path="/" component={SystemSidebar} />
<Route exact path="/Sound" component={Sound}/>
<Route exact path="/Computer" component={Computer}/>
</Switch>
You define here that the component SystemSidebar will only be loaded when you're at the Root directory ("/") of your App. It will be unloaded when you change that directory, for example, to "/Sound". SystemSidebar will be unloaded and Sound will be loaded instead.
Since your Sidebar should always be shown, it needs to be in your Main App and outside of your actual Router logic. Remember what the React Router does: It switches out components depending on which directory (which Sub-URL) you're in. It's best practice to have a Sidebar, an App Bar or similar things that are always there to be their own components. Your actual content should live in a separate container, where the needed component can be swapped out by the Router if need be. So something like this:
class MainSystem extends Component {
render(){
return (
<div className="MAIN">
<SystemSidebar />
<div className="ContentContainer">
<BrowserRouter>
<Switch>
<Route exact path="/Sound" component={Sound}/>
<Route exact path="/Computer" component={Computer}/>
{/* Route "/" should be last because it acts as fallback! */}
<Route exact path="/" component={StartPage} />
</Switch>
</BrowserRouter>
</div>
</div>
);
}
}
That's pretty basic but I hope you get the gist of it.
Also, I'd encourage you to take a look at an UI framework like Material UI for example. It already provides components ready for use (like your Sidebar which is called Drawer there), it's mobile first and easy to use with responsive design.
i'm currently developing a React+Redux front-end app and i'm running into a problem that seems inexplicable to me. When using a component, it works correctly from any other page but the homepage (the / route). When clicked on from the homepage, it crashes the app/browser.
I included react-router and react-redux in the tags because I use those libs, I don't know if they actually have any contribution to the cause of the problem.
I have the following setup:
src/index.js
...
render(
<Provider store={store}>
<BrowserRouter>
<Fragment>
<Header/>
<Switch>
<PrivateRoute path="/" exact={true} component={Home}/>
<PrivateRoute path="/profile" exact={true} component={Profile}/>
<PrivateRoute path="/settings" component={settings}/>
<Route path="/login" component={loginPage}/>
<Route path="/logout" component={logoutPage}/>
</Switch>
<Footer/>
</Fragment>
</BrowserRouter>
</Provider>,
document.getElementById('mount')
);
...
src/layout/footer.js
...
<div className="col">
<ul>
<li>
<Link to="/">Privacy</Link>
</li>
<li>
<Link to="/">Another Link</Link>
</li>
<li>
<Link to="/">Disclaimer</Link>
</li>
<li>
<Link to="/">Cookies policy</Link>
</li>
</ul>
</div>
...
export default Footer;
src/pages/home.js
...
render(){
var homePage = (
<Fragment>
<HeroComp />
<Explainer />
<StoreFinder />
<CalloutComp />
</Fragment>
);
return homePage;
}
...
export default connect(mapStateToProps, null)(Home);
Clicking on any of the components in the Footer should send me to the homepage. Which it does correctly if you are on any page but home.
But if you are already on the homepage and click on the link I expect it to either do nothing, but it seemingly crashes the app / browser. My best guess is that it somehow starts an infinite loop or memory leak but can't figure out why.
[Edit]
To clarify, there is no actual error. The browsertab just freezes and becomes unresponsive. The only way out is to close the tab.
Answering my own question (nobody could've figured this out without more information about the problem..)
After some more research/playing around I was able to find out that the problem was the requestAnimationFrame call in Map.js. Turns out, this is a google maps component (using google-maps-react) i've created that will initialize a google map element in the onComponentMount event.
However, if i'm on a page with the component mounted and try to link to that page from itself (for example, homepage and clicking on the logo or home button), the component tries to mount itself again (without properly destroying the existing google maps instance) and causes an infinite loop.
To "fix" this, I simply added a conditional statement to check if the map has been loaded.
if(!this.map) ) this.initMap();
I have a problem when trying to hide the navbar on my login page but i don't know how to do this.
You can see my code here:
render() {
return (
<Router>
<div >
<Nav />
<button type="button" className="form-submit_logout" onClick=
{this.handleLogout.bind(this)}>Logout</button>
<Route path="/" exact component={Login}/>
<Route path="/ChooseRole" exact component={ChooseRole}/>
<Route path="/DashboardGeek" exact component= .
{DashboardGeek}/>
<Route path="/DashboardAdmin" exact component= .
{DashboardAdmin}/>
</div>
</Router>
Please help me solve this problem. Thank you
This is because you have the component at root level, so it'd render in all pages.
Try including it as child to ChooseRole, DashboardGeek and DashboardAdmin components individually. That should solve the problem.
There are a few ways around this, it depends on the scale of your application but IMO the simplest is below.
I would assume you are using a boolean or a user object in your App or global state? Try adding a boolean to render the Nav like: {this.state.loggedIn ? <Nav /> : ''}
You can also generate an 'AppTemplate' component that passes the routes as props.children or includes it in every page.
I have an AngularJS background, and started to play around with React. I'm using the react-router and want have basically this setup:
Template
export default ( props ) => {
return (
<div>
<Navbar></Navbar>
{ props.children }
</div>
);
}
Routing in App.js
<Router>
<Route path="/" component={Template}>
<IndexRoute component={RestaurantRoulette} />
<Route name="workspace" path="workspace(/:workspace_id)" component={RestaurantRoulette}>
<Route name="run" path="run" component={Run}></Route>
</Route>
</Route>
</Router>
Navbar
<ul className="navigation">
<li>RestaurantRoulette</li>
<Link to="run">Run</Link>
<li>Save</li>
</ul>
What I want
When I am in localhost:8080/workspaces/444 I want to click on the Run Link, and navigate to localhost:8080/workspaces/444/run
What is the current status
If I manually type the url localhost:8080/workspaces/444/run, everything works fine. But when I click on the Link in the navbar, I get to localhost:8080/run.
Can somebody tell me how this works? How this is all connected?
I think the Link tag should be like that:
<Link to={'workspaces/' + this.props.workspace_id + '/run'}>Run</link>
you should get the workspace_id from path in Template, and pass it to the NavBar component
I have a component called Header which exists across all routes, while the rest of the app changes. So to accomplish this, my main render code looks about like this (using ES6):
render(){
return (
<div>
<Header></Header>
<Router>
<Route path="/" component={Home} />
<Route path="/details/:id" component={Details} />
</Router>
</div>
);
}
The challenge is that the contents of the <Header> should vary slightly depending on the route, for example a unique title per route.
How can this be achieved?
Thanks for all the great answers! Still mulling them over.
To throw another solution into the mix, I found that I can actually put arbitrary properties on the Route, so I added title:
<Route title="My Title" component={App} />
And I re-shuffled around my route hierarchy to include the header in Router (in the top-level Route component instead of outside any route as before), so my main render code looks like this:
<Router>
<Route component={App}>
<Route path="/" component={Home} title="Home" />
<Route path="/detail/:id" component={Detail} title="Details" />
</Route>
</Router>
And my App contains the header and is passed the current route's title:
class App extends React.Component {
render(){
var route = this.props.routes[this.props.routes.length - 1];
return (
<div>
<Header title={route.title} />
{this.props.children}
</div>
)
}
}
But I can't say this is the best solution. I do like that I can just put title on each route now, but I worry about the coupling and the way I have to extract the properties from the routes array.
This is a good use case for flux. You have route handlers that create an action when they mount. This action goes into the HeaderStore. The Header component listens to the Header store and renders based on it.
You can see an example of this here:
CurrentBoardStore holds the current page info
BoardPage updates the store when it mounts/updates
SubBoardHeaderWrapper renders the header with data from CurrentBoardStore
The way I do it (I'm pretty sure there is a better way, but this might help you out) looks like the following:
index.js
// define routes
const routes = (
<Route component={App} path="/">
<IndexRoute component={Home} />
<Route path="/" component={Home} />
<Route path="/details/:id" component={Details} />
</Route>
);
ReactDOM.render(<Router>{routes}</Router>, document.getElementById('your_id'));
App.js
render() {
return (
<div>
<Header routerProps={this.props.children} />
{this.props.children}
</div>
);
}
Header.js
componentWillReceiveProps(nextProps) {
// Get access to the pathname, it contains a string like "/details/"
console.log(nextProps.routerProps.props.location.pathname);
}
instead of putting header there.. put the header in a layout component. each view should use the layout component and you can pass whatever you want the header to be.
export class Layout extends React.Component{
render() {
return <div>
<Header title={this.props.title} />
{this.props.children}
</div>
}
}
all of your views can use this same component like so
export class SomeComponent extends React.Component{
render() {
return <Layout title="Some Component Title">
<div>my elements here</div>
</Layout>
}
}
NOTE: the beauty of using something like this, is you can set up any other default messaging like for instance lets say you want to have a flash message appear... someone clicks on something and you want a message to say 'You've successfully registered!' (in this example). You can include your flash messaging in the layout and simply dispatch an event to show the message. This can be done with modals too and really whatever your app requirements are :)