Nested routing in React router 4 - reactjs

I have a component, App component, NewHome Component and Results component.
My route in component App looks like
<Route exact path="/" component={NewHome} />
<Route path="flights/search/roundtrip" component={Results} />
NewHome Routes look like
<Route path="/flights" component={FlightSearch} />
<Route path="/hotels" component={HotelSearch} />
Results component is class based react component
Only App component gets render, but component inside, I can see null in react devtools and Ui only has the part of App not part of results
<Route path="flights/search/roundtrip" component={Results}>
null
<Route />
Ideally, it should work, but I am not able to understand what I am doing wrong here. Any pointers or suggetions will be helpful. What can be the probable reasons for this kind of behaviour
This is the code where i am doing reactdom.render in index.js file
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Route path="/" component={App} />
{/* {routes} */}
</ConnectedRouter>
</Provider>,
document.getElementById("root")
)
Note: full url is something like this
http://localhost:3001/flights/search/roundtrip?search_type=roundtrip&origin=BLR&destination=MAA&travel_date=2018-01-29&return_date=2018-02-13&class_type=ECONOMY&adult_count=1&child_count=0&residentof_india=true&infant_count=0&originAndDestinationSwapped=false&activeIndexMulticity=0

NewHome gets a match prop. With that, simply contact its url with nested routes you might need.
NewHome component:
render() {
const { match } = this.props
return (
<div>
<Route path={`${match.url}/flights`} component={FlightSearch} />
<Route path={`${match.url}/hotels`} component={HotelSearch} />
</div>
)
}

I think its because you forgot a '/' in your route.
try
<Route path="/flights/search/roundtrip" component={Results} />
instead of
<Route path="flights/search/roundtrip" component={Results} />
try exporting your component after wrapping your component in withRouter HOC
import { withRouter} from 'react-router-dom';
and export the component as follows
export default withRouter(Results);

I have found out the issue, in my app i was importing Route from react-router-dom. Its available in react-router. May be a correct error message would have helped :) Thanks #sujit and #mersocarlin for giving valuable inputs.

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.

Get router value from url in react

I have a react app, and a component that is supposed to load a set of value of the url
<PersistGate persistor={rootStore.persistor}>
<Router history={history}>
<ErrorBoundary>
<Preloader history={history} /> <== this need to read the url
<PasswordChecker></PasswordChecker>
<RouteDispatcher></RouteDispatcher> <== this contain the list of routes
</ErrorBoundary>
</Router>
</PersistGate>
In my RouteDispatcher path I have setup the route like this :
<BrowserRouter>
<Switch>
<PrivateRoute path="/ws/:ws/pj/:pj/ds/:ds" component={HomeDatastore} />
<PrivateRoute path="/ws/:ws/pj/:pj" component={HomeProject} />
<PrivateRoute path="/ws/:ws" component={HomeWorkspace} />
</Switch>
</BrowserRouter>
how would I get the value of ws=:ws pj=:pj etc...
I was thinking of doing it with a regex, but I wonder if there is a better way. Preloader is outside the routeDispatcher, so I don't seem to find a good way to get the value.
React router provides params into the component. Check props.match.params. I see that you have a custom component PrivateRoute so first check props in PrivateRoute.
May be try using useParams() hook. It gives you a way to access the URL params
Reference: https://blog.logrocket.com/react-router-hooks-will-make-your-component-cleaner/
Sandbox: https://codesandbox.io/s/react-router-useparams-hooks-q8g37
This might help you get started with it
import { useParams } from "react-router-dom";
function ComponentName() {
let params = useParams();
return (
<div>
{params.ws}
{params.pj}
</div>
);
}

ReactJS Context API - Redirect/Navigate programmatically

index.js - file
<AppProvider>
<BrowserRouter>
<div>
<Route exact path="/" component={Home} />
<Route exact path="/balances" component={Balances} />
</div>
</BrowserRouter>
</AppProvider>
On Home Component, I have a button and its event click was send to App Provider successfully.
<button className="btn btn-primary">{context.Process_ToBalances}</button>
And on AppProvider, I have a method like this.
Process_ToBalances()
{
//pre process data
// Redirect to Balance page here
}
ButI have no idea why all the redirection method failed.
I tried both of browserHistory.push('/Balances') and this.props.push("/Balances") but still failed.
In order to use react-router functionality in AppProvider, your Router must wrap you AppProvider component and you AppProvider must receive the Router Props, you can do so like
<BrowserRouter>
<AppProvider>
<div>
<Route exact path="/" component={Home} />
<Route exact path="/balances" component={Balances} />
</div>
</AppProvider>
</BrowserRouter>
and then in AppProvider use withRouter
export default withRouter(AppProvider);
After this you can use this.props.history.push('/balances), note the case sensitive pathname /balances and not /Balances
Refer this question for more details on How to Programmatically navigate using react-router

React Router v4 Nested Routing not working when in sub level

I'm trying to build a React App, this is the first time i'm working with react and so i don't know how to troubleshoot this. Basically what i need is to create routes for a link, let's say these are the following links.
/car
/car/sedan
/car/coupe
i have setup routing as so.
car.js
import React from 'react'
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'
import CoupeCar from './coupecar'
import SedanCar from './sedancar'
class Car extends React.Component {
constructor(props){
super(props);
}
render () {
return (
<div className="main_elem_container">
<Router>
<Switch>
<Route path="/car" component={Car} />
<Route exact path="/car/sedan" component={SedanCar} />
<Route exact path="/car/coupe" component={CoupeCar} />
</Switch>
</Router>
</div>
);
}
}
const Car = () => (
<p>I'm a Car</p>
)
export default Car;
And the routing works i can visit /car/sedan and /car/coupe when i'm browsing through the navigation from /car but i cannot visit /car/sedan when im at the /car/coupe page and vice-versa.
The navigation just gets stuck and doesn't load, please let me know on how i can fix this i've even tried many options but all of them give me this result, at least if i knew how to debug this it'll be better, thanks.
I don't know how your setup works partially, it should not with this config. What you need is:
<Router>
<Switch>
<Route exact path="/car" component={Car} />
<Route path="/car/sedan" component={SedanCar} />
<Route path="/car/coupe" component={CoupeCar} />
</Switch>
</Router>
So, if only when you hit /car your Car component renders. For /car/sedan and /car/coupe you will see the related components. If you don't use exact for /car, /car/sedan and /car/coupe will render Car component no matter what.
Also, do not use same component names. You have two Car components. Rename the container something else, App maybe?
try this, the exact path should be placed as last option
<Router>
<Switch>
<Route path="/car/sedan" component={SedanCar} />
<Route path="/car/coupe" component={CoupeCar} />
<Route exact path="/car" component={Car} />
</Switch>
</Router>

React Router 4 Navbar props not updating

I am building a small project to test the React Router 4. So far so good, my url updates and my props.locations shows up with withRouter. But I can't seem to change my navBar base on the props.location.
This is what my Routes look like:
<Provider store={ store }>
<BrowserRouter onUpdate={() => window.scrollTo(0, 0)}>
<div className="root">
<App/>
<Switch>
<Route exact path="/" component={HomePageContainer}/>
<Route eact path="/signin" component={SignInContainer}/>
<Route eact path="/reviews" component={Reviews}/>
<Route path="/favorites" component={Favorites}/>
<Route render={() => (
<p>Page Not Found</p>
)}/>
</Switch>
</div>
</BrowserRouter>
</Provider>
My component basically contains my HeaderBar and navBar, I have messages thats in navBar that I want to change so I would have title of the page, My App looks like this:
const App = (props) => {
let toRender = null;
if(props.location.pathname !== '/signin'){
toRender = (
<div>
<HeaderContainer />
<NavBarContainer />
</div>
);
} else {
toRender = null;
}
return(
<div className="App">
{ toRender }
</div>
);
}
I can import my navBar container into each of the routes i have for '/', '/reviews', and '/favorites'. But I don't think that would be a modular way to do it. I also have a shouldComponentUpdate lifecycle method inside NavBar, and I tested with a console.log to print something when it does update when I switch url, but it doesn't. Does anyone have any suggestions on a clean solution to pass in the props to my NavBar without importing it into every single one of the components? I also tried putting App component in the place of Route so I would have:
<App exact path="/" component={HomePageContainer}/>
<Route eact path="/signin" component={SignInContainer}/>
<App eact path="/reviews" component={Reviews}/>
<App path="/favorites" component={Favorites}/>
But then my Components aren't rendering besides the App. I'm not sure what's happening or why it's not rendering the components. Any suggestions would be much appreciate it. Thank you.

Resources