React Router, Page Not Found handling - reactjs

I have this setup for my routes:
<Container>
<Input />
<Switch>
<Route path='/' exact component={LatestMovies}/>
<Route path='/movie/:id' component={MovieWithDetails}/>
<Route component={PageNotFound}/>
</Switch>
</Container>
1.How would I need to setup the router, so that the Input component would be visible with all other components inside the Switch, except for the PageNotFound component?
I know that I could add the Input component inside the other components manually, but was wondering if there was a better way?
2.Inside the MovieWithDetails component I'm using fetch, where I fetch for a movie with an id, which I get from clicking on a link from the LatestMovies component through the router props.
The link, and any id can also be manually typed, so I was wondering, how would I need to handle the showing of PageNotFound component here?

1 - Did you try with multiple components? Something like:
<Switch>
<Input/>
<Route path='/' exact component={LatestMovies}/>
<Route path='/movie/:id' component={MovieWithDetails}/>
</Switch>
<Switch>
<Route path='/somepath' component={PageNotFound}/>
</Switch>
2 - I think that it's subjective. In my opinion I would show a feedback saying that there's no movie with the typed ID and I would not redirect to the PageNotFound component. But if you want to do it, with a simple `this.props.history.push('/somepath') will be right

Related

history.push() updates URL in browser but does not renders the appropriate component(Redirect not working too)

I am using react-router in my project and using this.props.history.push() inside componentDidMount() if the user is not looged in ,then redirect him to Login component.
This is my code in App.js:
class App extends React.Component{
constructor(props){
super(props);
}
componentDidMount(){
firebase.auth().onAuthStateChanged((user)=>{
if(user){
this.props.dispatch(login(user.email));
}
else{
this.props.history.push('/login');
}
})
}
render(){
//console.log(this.props.isLoggedIn);
return (<div className="App">
<Switch>
<Route path="/" component={()=><Homepage userName={this.props.username} logout={this.logout}/>} />
<Route path="/login" component={Login} />
</Switch>
</div>);}
}
export default withRouter(connect(mapStateToProps)(App));
Below is my index.js code:
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import './index.css';
const store= createStore(combineReducers({userState: userState}),applyMiddleware(logger));
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
,
document.getElementById('root')
);
Please not that I have removed some unnecessary functions and imports irrelevent to the context of this question.
Now let us come to what I have tried till now:
Using Router instead of BrowserRouter with history object passed to it.
Tried removing switch but that didn't work too.
I also searched for other questions that address this problem but unfortunately, none of them worked for me.
Please help me to find if there is any issue in my code.
Thank You!!
EDIT:
Looks like I'm having a bad day, even Redirect isn't working for me!
I'm trying very hard but I don't understand why Login component is not being rendered even when the URL is being changed.
You need to use exact prop in your Route. Otherwise, /login will also match / first
<Route exact path="/" component={()=><Homepage userName={this.props.username} logout={this.logout}/>} />
<Route exact path="/login" component={Login} />
It seems like you didn't use exact in your Route component. If you don't use exact then react will route to / path first. It will reads the path(/login) as / + login.
If you provide exact to this / path then react will route to the specified component which exactly matches the path. It is not necessary to provide an exact to all the Route component.
It is necessary when:
For example,
<Route exact path="/" component={About} />
<Route path="/login" component={Login} />
Then you need to provide the exact to parent path.
<Route exact path="/login" component={Login} />
<Route path="/login/profile" component={Profile} />
In short, if your next Route component path is a child of the previous path then you need to provide the exact property to the parent path.
Edited: There is one more thing that I'd like to say that in your Route component of / path you must have a render prop instead of a component prop for rendering the Callback component like this.
<Route exact path="/" render={()=><Homepage userName={this.props.username} logout={this.logout}/>} />
Some credits to #Sohaib answer. I have added some more explanation to his answer.

React Router: Changing components and routes by button

In a React application, there is a page with 2 buttons. Each button renders a different component, however, the route/URL doesn't change. How can I change the route, only changing the grey area? and How can I change the state if the user goes directly to the URL?
https://codesandbox.io/embed/elastic-fog-7hqk9?fontsize=14&hidenavigation=1&theme=dark
Yes, you can use Switch, it will render your component in case it matched the URL. Here is an example:
<Container>
<Header /> // this will render in all cases (Fixed here for example)
<Switch>
<Route exact path="/component1" component={Component1} />
<Route exact path="/component2" component={Component2} />
</Switch>
</Container>

How to render a component on each route with working functionality?

I'm trying to render a component that plays an mp3 on each route, and have the mp3 continue to play uninterrupted despite route changes. I'm not sure if this is possible, but i'd assume it is considering React creates single page sites.
So far i've only gotten as far as getting the component to render, my problem is all of the functionality along with the component has seized to work... I can't figure out why..
<HashRouter>
<Route path="/" render={props => <Music />} />
<Route exact path="/" component={Home} />
<Route path="/work" component={MyWork} />
<Route path="/about" component={About} />
</HashRouter>
The Route in question is the first one.
I've also tried this thinking I needed to pass down the props in order for the functionality to work
<Route path="/" render={props => <Music
changePlayingState={props.changePlayingState}
playing={props.state.playing}
showMusicMessage={props.showMusicMessage}
hideMusicMessage={props.hideMusicMessage}/>} />
A working version of the component(and how i would like it to appear on all routes) is shown on the home page(click play) code sandbox had a file size limit so i just added some popping sound effects(takes a second to start playing)
https://codesandbox.io/s/rlw1q45m1m
The only components regarding this question (and the only you'll need to view)are as follows:
Index.js
The routes: Home.js, About.js, MyWork.js
Music Component: Music.js
If any additional information is needed please ask! Thanks!
You just have to pull out the Music Component and make it a stand alone Component in itself then pass it down to the HashRouter within a single child and rest of the Routes.
Here is an example to implement NavBar in each route of the React App.

Is it a bad practice to use Navbar on all the available routes in React?

I always use Navigation bar on all of my screens.
Is it a bad practice to use Navigation bar on all the available screens ?
It's always recommended for Reusability to have repeated code in a separate component, and then reuse that component, so that you don't duplicate code. If you want to change something in that component you only have to change it in one part, not everywhere.
If you want that all your routes include this component, add it before the router, so it is always included. For example like this:
render(){
<div>
<h1>test</h1>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/messages" component={Messages} />
<Route path="/about" component={About} />
</Switch>
</div>
}
So test will always render no matter what route you choose.
If what you want is to create a layout, for example for private routes where you only show the navbar for logged in users (which is the most common scenario), have a look at this answer in SO.

React Router v4 Route difference

I am creating routing for my react app, could someone explain me difference between these two approaches.
From user point of view they work the same, what is the difference in performance, best practice?
First one is multiple Routes rendering different component for the same path:
<Route path='/:shop/booking' component={Services}/>
<Route path='/:shop/booking' component={Calendar}/>
Second is single path rendering components as props.children(?) :
<Route path='/:shop/booking'>
<Aux>
<Services/>
<Calendar/>
</Aux>
</Route>
<Route path='/'>
<Component>
</Route>
Is equivalent to :
<Route path='/' children={Component}/>
According to this : https://reacttraining.com/react-router/core/api/Route/children-func :
Sometimes you need to render whether the path matches the location or
not. In these cases, you can use the function children prop. It works
exactly like render except that it gets called whether there is a
match or not.The children render prop receives all the same route
props as the component and render methods, except when a route fails
to match the URL, then match is null. This allows you to dynamically
adjust your UI based on whether or not the route matches.
So by giving children prop instead of component to your route, you force it to render even if the current URL does not match. And I might be mistaking but it seems that adding a component prop to a route override its children prop.
Thus you cannot expect the same behavior for this two pieces of code :
<Route path='/:shop/booking' component={Services}/>
<Route path='/:shop/booking' component={Calendar}/>
Shows the two components for the specified path.
<Route path='/:shop/booking'>
<Aux>
<Services/>
<Calendar/>
</Aux>
</Route>
Shows the two components wrapped in another, for any path.
Finally, I would say that the best practice in React is to wrap your two components into one, and add it to the component prop of a route instead of creating two routes with the exact same path.
If you cannot wrap your two components because one has to be displayed on several routes, you can use something like the following :
<BrowserRouter>
<div>
<Header />
<Switch>
<Route path='/' component={Home}/>
<Route path='/foo' component={Foo}/>
<Route path='/foo2' component={Foo2}/>
</Switch>
<Footer />
</div>
</BrowserRouter>

Resources