I'm new to React, and have a n00b question and Googling hasn't turned up any good answers, maybe someone here knows:
With React Router or any kind of declarative routing, how do you "hide" private URLs that you don't want every user to know about? Or, if you just want to prevent someone from being able to easily enumerate all valid URLs?
Code splitting doesn't solve this (really) because the user is told what to request to get the rest of the information: I suppose you could put that URL behind authentication or some kind of restriction to prevent public access to it, but won't that cause the JS fetching the code-split import to receive an error, causing that error to bubble up and cause unintended failure behavior?
Edited to add:
The crux of my issue is wanting to essentially do <Route path="/:path"> where :path matches the entire URL path (e.g., could be foo/bar/baz) instead of just the first "part" (e.g., foo) for a URL like https://example.com/foo/bar/baz for example.
I could use <Route path="*"> and do my own URL parsing, but what if I don't want that route to actually handle the request? How do I signal "nope, I don't want this request, fall-through to the next matching Route"?
You can check if the user is authorized to see these routes and then show or hide them.
<Route path="/cities" component={City} />
<Route path="/courses" component={Courses} />
{user.hasPermission && (
<Route path="/secret" component={VerySecretPage} />
)}
If you want to go further, you can create a custom guarded route as described in this blog post.
Related
I'm building a deeply nested component that can be navigated to a lot of different ways, so I'm using a wildcard so I don't have to write out all the possible routes, something like:
<Route path={'**/post/new'}>
<CreatePost {...props} />
</Route>
However, now I need to get and use information from where the user came from in the create post component, so if their paths were:
/en/10249/discussion/13353/thread/24/post/new
/en/10015/discussion/14233/post/new
/fr/10350/link/155/post/new
I would need the params:
/en/:b_Id/discussion/:d_Id/thread/:t_Id/post/new
/en/:b_Id/discussion/:d_Id/post/new
/fr/:b_Id/link/:l_Id/post/new
including en, fr, discussion, thread and link, but because I replaced the params with a wildcard I don't have access to those params anymore. These are just a small subset of path examples that can reach /post/new, and why I needed to replace all routes with a wildcard.
I'm currently thinking of parsing the URL by splitting it by / and reading relevant data that way, but I'm looking if there are better alternative approaches.
Is there a way I can write this where I don't need to type out every possible route, while still splitting up the path into separate params?
react-router automatically includes path parameters in the component props.
In your <CreatePost> component, use:
this.props.match.params.d_Id
and so on.
For more info, see this question.
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.
So I have a localised site, and need to redirect routes in certain cases. For example, if a user had their UI set to Spanish, and went to mysite.com/about, they would need to be redirected to mysite.com/es/about.
So basically, the routes are duplicated via <Route> components from react-router:
{Object.values(UILanguages).map(locale => {
return (
<Route path={`${locale.basepath}`} key={locale.basepath} locale={locale} component={App} status={200}>
...routes go here
</Route>
)
})}
It would be simple enough to check within the componentWillMount lifecycle method of <App> to find out if I'm on the wrong <Route> component, but then I would have to prepend every single link in my application, which I don't want to do. Instead, I would like dynamic redirecting for foreign languages, as displayed in the first paragraph.
How is this possible?
Use the react router hooks instead it will be cleaner (smaller components) and simpler than using the component lifecycle.
https://github.com/ReactTraining/react-router/blob/master/docs/API.md#onenternextstate-replace-callback
in your case you need to use all 3 arguments because the first one is the next state the second is the redirection you want to make and the third is a callback to call when you are done testing values.
i can give you an exemple about how to make this but i don't really know how your app is structured so instead check this link , it helped me a lot:
https://www.youtube.com/watch?v=JicUNpwLzLY&t=359s
hope this helps!
ps: in some cases you need to define the onChange hook too example : pagination...
I'm trying to follow the react-router tutorial here trying to navigate using a child route and it doesn't work.
For example given this route definition:
export default <Route path="wifi" component={ Wifi }>
<IndexRoute component={WifiView}/>
<Route path="list" component= {WifiListView}/>
<Route path="connect" component={WifiConnectView}/>
</Route>;
If we are on "/" and navigate to "/wifi" we are good. The WifiView is presented however if if from "/wifi" we call hasHistory.push('list') it doesn't find the route. I would assume that the route is relative to its current location since I'm not using the "/" but that isn't being respected.
Also the document said:
You can also use the router that Router provides on "context". First,
you ask for context in the component, and then you can use it...
But I don't see anything that mentions how to do that in TypeScript so I'm using the static hashHistory.
It looks like novice questions but I had nowhere to ask since the suggested Discord channels are all dead... Can anyone help with those?
Thanks! Appreciate any help!
Ok finally found the replies to my questions...
As for today, react-router don't support relative navigation which means, it hast to navigate to the whole chain of routes and that is why hasHistory.push('list') doesn't work.
In order to get the context.router we need to use withRouter high order component but that isn't exposed on the TypeScript definition files they provide.
I am having trouble understanding some ReactJS and Redux principles,
In Redux we have a store and a set of reduceres that give your final state ( this part I get).
A Component receives props from their parent component, in case of Redux it passes the full state down with the Provider Component.
Now, lets assume this use case, I have my app state something like this :
auth : { name,id, ..etc} -> this is actually a JWT Token with a set of claims...
I am building a Container (Page) to be able to edit user profiles, the catch here , and where I am having trouble, I also want this page to handle a arbitrary user edit (in case admin is editing a account).
I am rendering my Page by using redux-router like this :
<Route path="/user" component={RequiresAuth(UsersPage) } >
<IndexRoute component={RequiresAuth(UsersOverview) }/>
<Route path="/user/overview" component={UsersOverview} />
<Route path="/user/account" component={AccountManagement} >
<IndexRoute component={RequiresAuth(AccountManagement) }/>
<Route path="/user/account/:userid" component={AccountManagement} />
</Route>
So reading the docs, I assume my props will have a userid which will override my default user if present.
Questions:
Is this approach correct? The Route Handling?
If I pass a userid param on the route, how do I proceed to load the user info and where? The way I see it, I need to dispatch an action, but I am not sure where to set the state , if on the store, or the component.
Also, Where would I load my user information? (constructor comes to mind)... or should it be in WillMount/WillReceiveProps?
Thank you.
If you really have a hard time to understand React/Redux principles i suggest you to try this tutorial :
https://github.com/happypoulp/redux-tutorial
It helped me a lot when i first started with React/Redux. By the way it's a bit hard to really answer your question because you re asking specific questions on a really specific case :)