React router rendering - javascript

I have a piece of code that I can not understand. Any explanation is appreciated. My question is what (props) actually is doing here and where it comes from? If I take "props" out, my code is not working.
<Route
exact
path='/'
render={(props) =>
!isAuthenticated ? (
<Landing {...props} />
) : (
<Redirect to='/admin' />
)
}
/>

Route is a component of react-router , which matches the url and routes , The code you have given is used for Authentication to redirect user if he is not Authorized ,whatever that is passed from parent has to be passed to the children in this case , the reason they are passing props is to pass the properties of React Router which includes things like history,location and etc. A simple console.log will show you what it is passing and why it is required
Refer to Image below to see what all is getting passed

Related

React router DOM : Switch not working if child element isnt a route

I'm facing an issue im not able to comprehend. I'm working on a React codebase and we started having too much Routes, so i decided to refactor it a little bit. There is some logic to determine if we should redirect to a route that will check if you're authenticated, and if not will redirect you to the sign in page. We also have basic routes without that mechanism that just display a component ( for every page that doesnt need authentication like sign in, forgot password etc. )
I've decided to abstract that logic in a different component, that will ultimatley render a Route. As a first step in that direction, i decided to wrap the rendering of every route in a single component, instead of having the whole logic just laying down there.
Here's the code for the routes:
<Switch>
{Object.values(userRoutes).map((route: SanitizedRoute) => (
<RouteController route={route} key={route.PATH} />
))}
</Switch>
RouterController.tsx
return (
<Route
path={props.route.PATH}
exact={props.route.EXACT}
render={() => {
return <Layout>{props.route.COMPONENT}</Layout>;
}}
/>
);
All the information for the Route component is passed down as a prop. This is where i start getting problems. If i try to access /path_b what is rendered is the first element of my userRoute array, eventhough the Route doesn't match; /path_b or /path_c or /path_whatever will always render the compononent defined for /path_a.
Accessing any path actually returns the /path_a component as if it was the only one present in my Switch component.
If i were to replace the RouteController component by its content as such :
<Switch>
{Object.values(userRoutes).map((route: SanitizedRoute) => (
<Route
path={route.PATH}
exact={route.EXACT}
key={route.PATH}
render={() => {
return <Layout>{route.COMPONENT}</Layout>;
}}
/>
))}
</Switch>
Then everything would work fine as expected. Wrapping my RouteController with a Switch component is also working fine - although im not sure about the side effect of having a Switch per route ?
{Object.values(userRoutes).map((route: SanitizedRoute) => (
<Switch key={route.PATH}>
<RouteController route={route} />
</Switch>
))}
My questions are:
Why isn't it possible to wrap all of those routes in a single ?
Is there some props from the component i should manually pass down to the component via my ?
I know that the is used to render the first route that match the path. Does having multiple kind of defeat the purpose of using a in the first place ?
Here's a Sandbox to display this behavior : https://codesandbox.io/s/react-router-dom-switch-test-forked-sqc98
I wrapper 1 route in a component. When using the wrapper nothing work. If i copy paste the wrapper content inside the Switch, it does.
Why isn't it possible to wrap all of those routes in a single
<Switch>?
This is because of how the Switch component works, it renders the first child <Route> or <Redirect> that matches the location.
Switch
The RouteController component is neither, so it's not involved in matching, and in fact only first component will be rendered. This is why you see only the first route rendered regardless of path.
Is there some props from the <Switch> component I should manually
pass down to the <Route> component via my <RouterController>?
It's possible to move/define a path prop (and any other "route" prop) on the RouteController the Switch may use for path matching, but this is ill-advised and not a documented use case of the Switch and Route components.
I know that the <Switch> is used to render the first route that
match the path. Does having multiple <Switch> kind of defeat the
purpose of using a <Switch> in the first place?
Yes, if you are rendering multiple Switch components, each with only a single Route component then they aren't really switching routes at this point and you're relying on the inclusive route matching of any wrapping router, or exclusively matching in the case of any other wrapping switches.
Solution
Instead of trying to map routes into your RouterController component and dealing with the issues of what is directly composing what, have your RouterController consume all the routes as a prop, and render them into a single Switch component.
RouterController.tsx
const RouterController = ({ routes }) => {
// any component business logic
return (
<Switch>
{routes.map((route: SanitizedRoute) => (
<Route
path={route.PATH}
exact={route.EXACT}
key={route.PATH}
render={() => <Layout>{route.COMPONENT}</Layout>}
/>
))}
</Switch>
);
};
...
<RouterController routes={Object.values(userRoutes)} />

react router private route implementation is path props passed or not?

Hello everyone this is more of a request asking review on my understanding of private route implementation in react.js rather than a question. Many implementations of private routes with authentication seem to have this same code posted
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
fakeAuth.isAuthenticated === true
? <Component {...props} />
: <Redirect to='/login' />
)} />
)
now as far as I know the 3 dots is the spread operator which basically assigns the passed object . Further SO answer also corroborates it What do these three dots in React do? especially the answer from Mehdi Raash . Now my understanding is that this basically means that the 'path' prop is also passed in the ...rest object just like
<Route path={rest.path} render={(props) => (
fakeAuth.isAuthenticated === true
? <Component {...props} />
: <Redirect to='/login' />
)} />
but recently i have came across this post https://ui.dev/react-router-v4-protected-routes-authentication/
and this line just doesn't make sense to me
A few notes on the above code. With Route, if a path isn’t supplied, then that Route will always match. So in the case above, because we didn’t supply a path prop, the Route will always match which means the render prop will always be called.
So I just want to know whether this statement is correct and path props isn't being passed in the private route implementation and if so then what is this {...rest} doing in the Route.
Note: No offence for the author behind the article i just want my understanding stand corrected if I am wrong
The spread operator basically puts all remaining properties into a new object called rest in your case. Let's say that PrivateRoute was initiated like this:
<PrivateRoute component={Home} path="/protected" secure={false} />
component was desctructured and renamed to Component in your example. This means that rest looks like this:
rest = {
path: "/protected",
secure: false
}
What happens next is that the rest object is spreaded into the Route. So this:
<Route {...rest} render={(props) => ()} />
is the same as this:
<Route path={res.path} secure={res.secure} render={(props) => ()} />
or this:
<Route path="/protected" secure={false} render={(props) => ()} />
By the way, the props in the render() function only contain three props and they come from the Router:
match
location
history

Why aren't {...props} necessary when passing them to a component in a router in React?

I've started learning React recently and when I was following a course I stumbled upon something that confused me a lot. I decided to try it out separately to understand it and I don't get it.
I've looked it up and when you use a Route Tag in React, you do it like this:
<Route path="/" component={Component} />
But when you want to pass props, the syntax is the following:
<Route path='/dashboard' render={(props) => (
<Dashboard {...props} isAuthed={true} />
)}
/>
Which at first confused me, but then I got it. When testing, I did the following:
<Route path="/" render={() => (
<PropDrilling user={"Someone"} />
)}>
When I test this in the component, console.logging this.props, it works and I don't know why.
Thanks in advance!
React router uses React's render props where react router uses match, location and history. So, when you don't provide props in the render function, it will still be able to show you react component props. But it would not include match, location and history props provided by react router. To use them, you must provide props parameter in the render function.
Using an analogy, consider
function test(obj) {
console.log(obj);
}
test({a : 1});
function test() {
console.log({a : 1});
}
test();
These two functions log the same result, but one has no information about the data passed to console.log, while the other does.
Similarily, {...props} means you are forwarding the props received as a function parameter to a component, irrespective of what the value of props is.
Taking the example of <Route path="/" component={Component} />, you could write it like that :
const myProps = {
path: "/",
component: <YourComponent />
}
<Route {...myProps} />
Consider every props passed to a component as a key in an object. Here is a small repro on Stackblitz to show you how it works.

Why pass {...props} in a React Route?

I'm using the route below just because it was the code I found on the web:
<Route exact path="/test" render={(props) => <Test {...props} msg={ "abc" } /> } />
I know the {...props} denotes multiple arguments but I can't understand why I need it at all because the code below also works just fine and props.msg is available in Test expected
<Route exact path="/test" render={() => <Test msg={ "abc" } /> } />
So what does {...props} actually do when passed in during render?
From the documentation:
The render prop function has access to all the same route props (match, location and history) as the component render prop.
If Test is not using any of these then you don't have to pass them.
For your use case, your second code snippet is sufficient and will work just fine.
Spreading the props into the child component like
<Route exact path="/test" render={(props) => <Test {...props} msg={ "abc" } /> } />
makes sense if you want to pass properties to the child component that you are not handling yourself but receiving from another source like the Route-Component itself (in particular the route props "match", "location" and "history").
Keep in mind you can have parameters in your route, ex:
/users/:username
Those props will allow you to access it. In your case, you probably don't need it but you're better to always include them so it's consistent.
https://reacttraining.com/react-router/web/api/Route/route-props
more doc about the 3 props that are provided :
match https://reacttraining.com/react-router/web/api/match
location https://reacttraining.com/react-router/web/api/location
history https://reacttraining.com/react-router/web/api/history

React-router-dom: Render two components under one route

<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<Route
path="/test/:id"
component={Question1} />
<Route
path="/test/:id"
component={Question2} />
</div>
</BrowserRouter>
</Provider>
I need to navigate from Question1, which has id of 1, to question2 which should have id of 2. I can navigate to question1 from test without problem, but when I want to go to question to using this.props.history.push('/test/2') the URL changes but I dont get navigated to question2.
Any ideas ?
You should have a single Question component which handles the id and displays the appropriate question.
You can do the same using the render method of Route
<Route
path="/test/:id"
render={(props)=>{
switch(props.match.params.id){
case "1": return <Question1/>;
case "2" : return <Question2/>;
default : return null;
}
}}
/>
EDIT: Corrected code after comment
This issue is that <Switch> will match the first component under the <BrowserRouter/> in this case. If it doesn't match the first path, it will try the second. That is how Switch works. So in your example the path template of path=/test/:id actually matches both /test/1 and /test/2, and that Route always renders Question1 and never Question2. Check out the docs here
https://reacttraining.com/react-router/web/api/Switch.
One quick thing you can do is if you know you only have two Questions then you can do
<Route exact
path="/test/1"
component={Question1}
/>
<Route exact
path="/test/2"
component={Question2}
/>
Which will solve your problem immediately, but I don't recommend this as it gets unreasonable as you get more Questions. A better set-up is what #palsrealm recommends.
You can do this with one route, and load the right question in your component :
<Route path="/test/:id" component={Question} />
// Question.js
export default React.createClass({
render() {
return (
<div>
<h2>Question {this.props.params.id}</h2>
</div>
)
}
})
doc : route-with-parameters

Categories

Resources