ReactJS - two way router - reactjs

I wanted to ask if something like two way router. I found the router-react, but I do not like setting of Link directly to path in string format. Like
<Link to="/about">About</Link>
Exist router to refer to a component instead of the path? Like
<Link to={AboutComponent}>About</Link>
If you would like to change the URL, so I had to change paths everywhere. Thus, I will change it in one place.

Exist router to refer to a component instead of the path?
No.
If you would like to change the URL, so I had to change paths
everywhere. Thus, I will change it in one place.
Then store all your paths as a constant in one place, and import them where you need them:
// paths.js
export const ABOUT = '/about';
export const SOMETHING_ELSE = '/somethingelse';
So in your app:
import { ABOUT, SOMETHING_ELSE } from './paths'
...
<Route path="{ABOUT}" component={AboutComponent}/>
And just do the same wherever you use <Link to={ABOUT}>About</Link>
If you at some later point decide to change the path, just change it in paths.js.

IMO, we cannot use component itself directly in Link component, simply because in some case one component may be shared in different routes. Like the following:
<Route path='/recent-movies' component= {MovieList} />
<Route path='/hot-movies' component= {MovieList} />
In the above example, the MoveList component will display the movies based on the path passed in. (I knew it is not a good example, just want to show it is a possible case)
In angular ui-router, it introduced route name, so every time it will not be affected when path is changed. But in react-router, it is simpler, I personally don't mind to much.

Related

React - How to navigate using a custom component instead of react-router-dom

I have a rooms list that I iterate, rendering the different rooms like this:
<Room room={room} key={room.id}/>
I want each room to redirect to their corresponding path (/rooms/:id). The only way of redirecting elements is via react-router-dom but I feel there must be a better way of achieving redirection in this case.
React router works fine, only thing you need is pass id to link
<Route path="/RoomsList/:roomId" element={<RoomCard/>}/>
in RoomCard you use hook
const {roomId} = useParams();
and change the component depending on the id.

ReactRouter: Capture part of path and specify a value to match at the same time

I have a path in the format of /somePath/:name where the :name needs to be one of the known strings. I read that the Route component supports an array of URLs to match, so it's easy to generate a list of supported paths, but doing so takes away the convenient of automatically capturing the part of the path.
For example, I currently have a Route component defined like this below.
<Route exact path={SomeKnownNames.map(n => `/somePath/${n}`)} component={SomeRoute} />} />
This should work, but I cannot access the value in props.match.params in the Route component anymore. Is there a way to achieve this without manually parsing the URL?
Let me add that I do NOT want to match if the value of :name is not in the known strings.
I'm using react-router-dom v5.
Apparently, this works:
<Route exact path={SomeKnownNames.map(n => `/somePath/:name(${n})`)} component={SomeRoute} />} />
i.e. If you do /somePath/:name(foo), then it matches /somePath/foo, and foo is captured in props.match.params.name in the route component.
Have you tried doing this:
path={SomeKnownNames.map(n => `/somePath/:${n}`)}
Adding the : allows the names to be treated as params. Try it out, let me know if that is unrelated. I may need more information to better answer your question.
Also, try to use the useParams() hook from React Router. What does it show in your component?
Update: path-to-regexp was removed from React V6, so this answer is obsolete.
React Router uses the path-to-regexp library, which allows you to provide your own regex for a parameter. See https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#custom-match-parameters.
For example, if you want to match /somePath/foo, /somePath/bar, and /somePath/baz, and have match.params contain myParam: "foo", myParam: "bar", or myParam: "baz", use this pattern:
<Route
exact
path="/somePath/:myParam(foo|bar|baz)"
component={SomeRoute}
/>

Update react Context from children or alter component structure?

This question focuses on the Header component: So in my app. I have a something that looks a bit like this with a few more components and plenty more routes:
return (
<SiteLayoutContext.Provider value={siteConfiguration} >
<div className="topContainer">
<BrowserRouter>
<div className="header">
<Header/>
</div>
<div className="mainWrapperContainer">
<div className="contentWrapper">
<Route exact path='/login' component={Login}/>
<Route exact path='/home' component={Home}/>
<Route exact path='/about' component={About}/>
<Route exact path='/collection/:collectionName' component={CollectionLandingPage}/>
<Route exact path='collection/:collectionAlias/id/:itemId' component={ItemView}/>
</div>
<Footer/>
</div>
</BrowserRouter>
</div>
</SiteLayoutContext.Provider>
)
What I'd like to accomplish:
I can currently set a logo for the header. This is great, but I'd like the user to be able to select a custom logo, one that will replace the global default, if they are within a collection.
To define 'within' a collection, the route will start with /collection/ so in the example below, both the ItemView and the CollectionLandingPage should have this custom logo.
I need to be able to tell my header that it's currently within a collection, and I want to limit renders and redraws to improve performance. I have an api endpoint that I fetch to grab collection info, which would tell me if a custom logo has been set, and what the href should be.
What I've tried: My original thought was to rip the header out of the main return() shown above, and instead place it within every component, that way I can use React's useLocation() hook to check the the url, and only fetch the collection's logo if1) we're within a collection and 2) a custom logo has been set.
My issue with this method is that I now have to enter the Header within every single component, instead of how it is currently implemented.
My second thought is to wrap it all within some kind of context, consume it in every component, and update it based on the url, which would then fetch the relevant info, but this just feels off. I suppose I could fetch the information for the collection and update a new CollectionInfoContext every time I hit /collection/:collectionName which would only update when the collectionName changes?
I'm kind of just looking for suggestions on how to implement this because well... I feel like there's an option I haven't listed that's probably much easier and more reasonable... as is the stackoverflow standard.
Disclaimer: I'm a react context api novice
If you're not using Redux or another state management solution, a context is definitely a reasonable way to share data across your application, UI themes are actually one of the recommended use cases in the React docs.
The only issue I could see is that changing the state for only certain components would require a way of resetting the context for other places where you just want a standard logo. But reading the URL name could solve that by simply storing the path name and using it inside of Header to render the custom logo if the path includes collections. If Header can read the path name all on its own, you may not even need a context if that's the only condition that's dynamic everywhere.

Using multiple params with React Router?

<Route path="/:user" component={Home}>
<Route path="/:thing(/:version)" component={Thing}/>
</Route>
So, I've got two dynamic objects in my application that I'd like to be controlled by route params in react-router. Using the code above, both /0 and /0/3 take me to Home. I need /0/3 to take me to Thing. I'm not sure what I'm doing wrong here... Does react-router even support multiple dynamic params next to each other like this? I couldn't find anything in the docs.
What happens here is that you've given React Router two paths that can both match on /anything. By default then React Router matches the first one it can find.
To dig deeper, if I go to /pudding, React Router can't know if you meant /:user or /:thing. Since /:user occurs first, that option will be chosen.
You also need to make sure if nesting routes is what you want. Currently, your Thing route is nested below Home, which means that it is rendered via this.props.children in your Home component. So, for your current Thing route, Home will always be rendered too, with Thing as a child. If your Home component doesn't render this.props.children, Thing will not be shown.
I suspect you just want two different pages. What you could do to achieve that is the following:
<Router history={history}>
<Route path="/user/:user" component={Home} />
<Route path="/:thing(/:version)" component={Thing}/>
</Router>
This will make every /user/name go to the Home component, and every other /random (with an optional extra level) will go to Thing. If you wonder why in this case React Router doesn't take /user/name to the Thing route, it's because it still matches in the order your routes are specified. Because your Home route matches the requested URL, no siblings of this route are tested anymore.

react-router: how to get the previous route in onEnter handler?

I'm trying to find a way to read the previous route/path when a user hits a new one, within the onEnter handler.
I have a React Router structured like so:
<Router history={history}>
<div className="index">
<Route
path="/"
component={ComposedAppComponent}
onEnter={this.onEnterHandler.bind(this)}
>
<Route name="streamKey" path=":streamKey">
<Route name="articleUri" path="(**)" />
</Route>
</Route>
</div>
</Router>
the function, onEnterHandler, looks like so:
onEnterHandler(nextRouteState) {
const { streamKey, splat } = nextRouteState.params;
const nextPath = `/${streamKey}/${splat}`;
const prevPath = // HOW DO I GET THE PREVIOUS PATH?
}
I can't seem to find a way to read the previous route path the user was on... I need to make a comparison between the new route and previous one. Any input on how to approach this is much appreciated. :)
Cheers!
Are you trying to get the previous path when the user navigate to new path through address bar or using react-router like this.context.router.push()?
If the navigation is using react-router, maybe these simple way would get what you're looking for:
1. Pass prevPath using query-params
Navigation might look like this:
`<Link to="/next" query={{ prevPath: this.props.location.pathname }}> Your Next path</Link>`
Now, you can get your prevPath value on onEnterHandler method by using this:
nextState.location.query.prevPath
Cons: the query will appear on address bar.
Pros: you are still able to get your prevPath when user is navigating through address bar (direct path access).
2. Pass prevPath using state
Navigation might look like this:
`<Link to="/next" state={{ prevPath: this.props.location.pathname }}> Your Next path</Link>`
Now, you can get your prevPath value on onEnterHandler method by using this:
nextState.location.state.prevPath
Cons: the query won't appear on address bar.
Pros: you are not able to get your prevPath when user is navigating through address bar (direct path access).
In addition, the cons of those method is you should assign a prevPath value into each of Link components. Create a wrapper component for the Link component would solve this issue.

Resources