How to get parent parameter in nested route? - javascript

I have the following basic structure of nested routes/components for a react app:
/users -> UsersList
/users/:id -> UserLayout
/users/:id/ -> UserProfile
/users/:id/settings -> UserSettings
/users/:id/blah -> YetAnotherComponent
What I'm trying to figure out, in the context of react router v4, is how to access the :id parameter in the UserSettings component. I can access it fine in the UserLayout component, but nowhere else further downstream.
My main router definition lies in a Home component, but all the user-specific routes contain the same header information so I want all the user-specific routes to be nested. My current structure has these nested routes defined in the UserLayout component. However, no matter what I do to the layout component's route definition, I cannot get any other route than the "index" route (UserProfile) to render. When trying to access UserSettings or any other route, my top level 404 route gets hit instead.
Here's the relevant JSX (snippets of the actual components' render functions):
Home
<main>
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/users" component={UsersList} />
<Route exact path="/users/:id" component={UserLayout} />
<Route exact path="/" component={Home} />
<Route component={NoMatch} />
</Switch>
</main>
UserLayout
<div>
<Switch>
<Route path={`${match.url}/settings`} component={UserSettings} />
<Route path="/users/:id/blah" component={YetAnotherComponent} />
</Switch>
<Route path="/users/:id" component={UserProfile} />
</div>
In the UserLayout component I've tried both path formats shown in the Switch and have tried turning on/off exact matching. The only thing I can come up with is using the Route component's render parameter to pass the id parameter, but that just seems wrong. Is that my only option?

Of course only a few minutes after I post, I figured it out. In my top-level route config, I had set the /user/:id to require an exact match. This meant that when I navigated to /user/:id/anything_else react router didn't load UserLayout at all and thus didn't get the chance to test the remainder of the routes I had configured.
So I changed this from my home component:
<Route exact path="/users/:id" component={UserLayout} />
...to this:
<Route path="/users/:id" component={UserLayout} />
... and now all is well with the world.

Related

React Router: Exclude strings from generic `/:slug` path

I'm attempting to setup the following UI based on routes
/things/ - display the list of items (Component A)
/things/new - display the list of items, with a modal form overlay (Component A and Component B)
/things/:slug - display a specific item (Component C)
With this setup:
/things/ would display Component A
/things/new would display Component A AND Component B
/things/:slug would display ONLY Component C
This is all in a nested route - /things. Which means the path from useRouteMatch returns /things
I have tried several combos with Switch, and attempted to use matchPath and useRouteMatch to differentiate /new from /:slug but with no luck. Every attempt results in one of the paths not returning the correct component.
For example:
<Switch>
<Route path="/:slug(exclude "new")" exact component={ComponentC} />
<Route path="/" exact component={ComponentA} />
</Switch>
<Route path="/new" component={ComponentB} />
Another option I've tried with no luck is to use regex on the slug, to match the slug pattern. Let's says the slug pattern is xxx-xxx-xxx-###, then I tried:
<Route path="/:slug((\w*-)*(\d*))" component={ComponentC) />
but this doesn't match the routes for some reason.
I'm thinking I could use location.pathname but this seems like a hack.
Is it possible to match and render routes as described above with standard <Route path="[something]" /> components with this setup?
This is why I love Stack Overflow. Asking the question often leads you to answer it yourself.
I was browsing the React Router docs for info on the regex and discovered the Route path property can accept an array of paths. So I went with this:
const { path } = useRouteMatch()
<Route path={[path, `${path}/new`]} exact component={ThingsList} />
<Switch>
<Route path={`${path}/new`} render={() => <NewThing path={path} />} />
<Route path={`${path}/:slug`} render={() => <Thing path={path} />} />
</Switch>

What exactly is <switch> used for in React Router?

I am new to React learning , and was trying to build an app using react-router-dom. I was able to implement basic routing when I came across the term 'switch'. Can anyone please explain me with a use-case example where we use switch and what is its use?
Since you are new am going to take a bit more time to explain with examples and also add some few things about it you may want to have handy.
So like Iddler said, Switch is more or less like the "Switch case" condition in any other languages but usually ends with the first match it finds.
<Switch>
<Route path="/home" component={Home} />
<Route path="/about" component="{About} />
</Switch>
That is an example of its most basic use. Switch determines the start and end of the block or condition. Each Route checks for the current path. supposing we were working on "www.test.com". All of "www.test.com" is the root "/". So the Route checks for the path after the root. so if you had "www.test.com/home", "/home" comes after the root so the "Home" component will be loaded in our example above and if you had "www.test.com/about" the "About" component is loaded.
Be mindful that you can use any names. the components and paths do not need to be the same.
There are also cases when you might want to use exact to match an exact path. this is useful when you have similar paths. eg "/shop" and "/shop/shoes". using exact ensures Switch matches exact paths and not just the first.
Eg:
<Switch>
<Route exact path="/shop" component={Shop} />
<Route exact path="shop/shoes" component="{Shoes} />
</Switch>
You can also use <Route... /> without the <Switch>.
Eg:
<Route path="/home" component={Home} />
so unlike direct component loads where you just load a component like <Home /> Routers work with the URLs.
Lastly, the <Route... /> path can take arrays of url to load same component.
Eg:
<Switch>
<Route path={[ "/home", "/dashboard", "/house", /start" ]} component={Home} />
<Route exact path={[ "/about", "/about/management", "/about/branches" ]} component="{About} />
</Switch>
I hope this helps. Let me know if you need clarifications of any sort. :)
UPDATE:
You are not required to write Routers in this same format always. below is another format you could use;
<Router>
<Switch>
<Route path="/home">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
</Switch>
</Router>
There are instances like am in now where you want to be able to handle what shows when a wrong URL is entered. like a 404page. you could use Router without a path. so like a regular switch statement, that becomes your default.
<Switch>
<Route path="/home" component={Home} />
<Route path="/about" component="{About} />
<Route component="{PageNotFound} />
</Switch>
Switch looks through Route's children and renders the first one that matches the current path, once it does it will not look for any other matches.
The Switch component will work much in the same way as the Router component, meaning we will still have nested Route components that need exact paths, etc.
The added functionality of Switch is that it will only render the first matched child. This is really handy when we have nested routes such as the below:
<Switch>
<Route path="/accounts/new" component={AddForm} />
<Route path={`/accounts/:accountId`} component={Profile} />
</Switch>
Say we put the above code in a component — we would see that both {AddForm} and {Profile} would render, since “/accounts/new” could look like either Route to a Router component. Router components render inclusively of all route matches. The Switch component will render exact matches, and only the exact match. This makes it ideal for these nested scenarios.

React query strings take more than 1 parameter?

I'm working with React.js Router and trying to achieve this:
Users go to a module then a level and the url will look like this:
myapp.com/game/module/some-module/level/level-1.
I need to handle all different module and level like this:
/module/:name
/level/:name
so I don't need to specify each url several times.
Here's the code in App.js:
const App = () =>
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/game" component={Game} />
<Route exact path="/module/:name" component={Module} />
<Route exact path="/level/:name" component={Level} />
<Route component={NoMatch} />
</Switch>
</Router>
export default App
I know I can "grab" the value of module name in the child like this: match.params.name.
How do I do it in React?
Do you have a better approach than this?
e.g. /game/some-module/some-level, in this case, how do you pass the module name and level name in the Route
It looks like the Level route should be nested inside the Module component.
Here's a similar discussion on Stack Overflow: Nested routes with react router v4
And here's a good blog post that explains how nested routes work in React Router v4: https://tylermcginnis.com/react-router-nested-routes/

How do I pass Redux props to a separate component in a different Route with React Router v4?

I'm creating a real time chat app with react, react router v4 and redux. The problem is that, as you may know, react router v4 changes a lot of stuff and nesting routes is not working for me.
So I have this code with a nested route that is not working:
<Route path='/' component={App}>
<Route path="/user" component={AddUser}/>
</Route>
It's giving me this error Warning: You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored.
Where component={App}, in the first <Route path='/' is a redux connect:
const App = connect(mapStateToProps, mapDispatchToProps)(Header)
So my component App has all the the props I need. All works fine except by the fact that I need to pass those props from App to the nested route component AddUser.
How do I pass the redux props to a separate component in a different Route?
With react-router v4 when we need nested routes, we don't nest Routes. Instead, we put them inside nested components. You can read about this more here: Nested routes with react router v4
In your case, you can put your "/user" route inside the App component and then use render instead of component. So you can pass your App props to AddUser as usual.
<Route path='/' component={App}/>
App.js
class App extends Component{
//...
render(){
return(
//....
<Route path="/user"
render={() => <AddUser myProp={this.props.myAppProp}/>}/>
//....
)
}
}
I found a solution reading this blog post: https://medium.com/#5XvYrLT7/react-server-side-rendering-with-react-router-3-x-74cf87919b3
With react-router v4 you don’t do this any more, don’t nest :
<Route component={App}>
<Route component={Index} />
<Route component={About} />
</Route>
You do it like this now. App embeds your routes as-is:
<App>
<Switch>
<Route exact path="/" component={Index} />
<Route path="/about" component={About} />
</Switch>
</App>

Default Route With React Router 4

fI currently have the following routes defined in my application:
/
/selectSteps
/steps
/steps/alpha
/steps/beta
/steps/charlie
Which could also be visualised like this:
- (home)
- selectSteps
- steps
- alpha
- beta
- charlie
My root component looks like this:
<Route path="/" exact component={Home} />
<Route path="/select-steps" render={() => <StepSelectorContainer />} />
<Route path="/steps" component={StepsContainer} />
My Steps component does this:
steps.map(step => (
<Route
path={fullPathForStep(step.uid)}
key={shortid.generate()}
render={() => <StepContainer step={step} />}
/>
This all works nicely, but I don't want steps to exist as route in its own right. Only its child routes should be visitable. So I'm looking to lose the /steps route to leave my routes as:
/
/selectSteps
/steps/alpha
/steps/beta
/steps/charlie
How should I configure my routes for this? Ideally, hitting /steps would redirect to the first child route.
Actually, it's pretty straightforward...
Use Redirect component to... well, redirect.
<Redirect from="/steps" exact to="/steps/whatever" />
exact prop guarantees you won't be redirected from sub-route.
Edit: after all, Redirect does support exact (or strict) props. No need to wrap in Route. Answer updated to reflect that.
Pedr,
I think that this will solve your problem.
<Route path="/" exact component={Home} />
<Route path="/select-steps" render={() => <StepSelectorContainer />} />
<Route path="/steps" component={StepsComponent} />
And then in your StepsComponent render method, you can do this.
<Switch>
{steps.map(step => (
<Route
path={fullPathForStep(step.uid)}
key={shortid.generate()}
render={() => <StepContainer step={step} />}
/>}
<Redirect from="/steps" exact to="/steps/alpha" />
</Switch>
What this will do is render your steps component because it the route begins with /steps. After that is rendered, then it will render one of the nested routes based off the url. If the url is just "/steps", then it will redirect to the initial route listed here, in this case "/steps/alpa" by rendering the redirect. The Switch will make it so that it only renders one of the routes.
Credit to Andreyco for the redirect code.
I hope this helps.

Categories

Resources