Render Props - React Route - reactjs

const Home = () => <div>Home</div>
const App = () => {
const someVariable = true;
return (
<Switch>
{/* these are good */}
<Route exact path='/' component={Home} />
<Route
path='/about'
render={(props) => <About {...props} />}
/>
</Switch>
)
}
const About = (props) => {
return (
<div>
About
</div>
)
}
In the code sample , at
<Route
path='/about'
render={(props) => <About {...props} />}
/>
when react encounters the render prop of the Route component which is part of react-router, what does it pass a props?
Given the documentation at https://reactjs.org/docs/render-props.html ,
a render prop is a function prop that a component uses to know what to render,
is the value passed a props buried inside the declaration of Route in react-router

The props are passed to the render prop method by the Route component. You can see this in the React Router source code. The props passed by the Route component have match, location, history, staticContext. If you want to use props from the parent component, where you are defining the render props method then you can omit the props argument.
render={() => <About {...props} />}
Then you would get the props from the component that contains the Route.
The example you have provided doesn't make much sense since that replicates the behaviour that you get by just using the 'component' prop on the Route.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Route.js#L120

We use Route with render props as,
<Route path = "/about" component={About} />
OR,
<Route path = "/about" render= { (props) => <About {...props} } />
The second one is different from the first one in the sense that in the second case, the About component has access to the props coming through the Route.
Say, for instance,
there is a Profile component,
<Route path="/admin/profile"
render={ props => (
<Profile tabs= {"valuePassed"} {...props} />
)}
/>
Now in Profile component, we can access all the props,
this.props.tabs give "valuePasses" in class-based component while props.tabs is used for functional component.
Hope this helps.

You get react router default props while passing props in render method just like if use component instead of using render props which implicitly get all these props match, location, history and staticContext. and you need to provide props as an argument otherwise it render method won't pass props down to the children because it will consider it undefined.
Here is working example for render props in react router:
https://codesandbox.io/s/72k8xz669j

Related

Protected Routes using React

Going through resources on creating protected routes in React, I came across the following example
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
fakeAuth.isAuthenticated === true
? <Component {...props} />
: <Redirect to='/login' />
)} />
)
I can make use of the component by
<PrivateRoute exact path="/books" component={Book} />
So I've the following doubts on the above code segment
The protected route is passed as component but why's the Component tag used?
Also, if the rest of the properties are passed as ...rest to Route where does the render method gets its props from?
In Javascript, we can rename the key while destructuring using colon(:) as in component:Component The reason to do this is we cannot directly destructure Component since it is a reserved keyword in React.
2.
...rest is props for Route component. For example: In, <Route exact path="/books" component={Book} />, 'exact' and 'path' are props for Route component not for Book.
To pass props for Book component, react-router allows us to use render prop. render prop accepts a function and returns a component. react-router handles the passing of props from Route component to our Book component for us.

How to pass data from functional component to class component in reactjs?

I want to pass data from my App.js file to a sub class component.I have tried with props,but its not workng at all
const org = 'Organization Dashboard'
const remotes = 'FromAdmin';
const DashboardContainer = ()=>(
<Sidebar org={org}>
<div className="App">
<Route path='/dashboard' component={Dashboard}/>
<Route path='/mytab' component={MyTab}/>
<Route path='/team' component={MyTeam}/>
<Route path='/settings' component={Settings}/>
<Route path='/logout' component={Logout}/>
<Route path='/team-settings/:param1' component={TeamSettings}/>
**<Route remotes='remotes' path='/user-settings/:param1' component={MyTab}/>**
</div>
</Sidebar>
)
I want to pass data in MyTab class component, when i use this.props in myTab , its showing undefined
Help will be appreciated
I assume you're trying to pass remotes='remotes' to MyTab. Components rendered by a Route are passed only the route props, but you can use an anonymous inner function to slip in extra props. Don't forget to pass on the route props if you need them!
<Route
path='/user-settings/:param1'
component={routeProps => <MyTab {...routeProps} remotes='remotes' />}
/>
You can override the component. By default, component accepts a class-based or function-based component. But you can override the component. Not only data you can pass functions as well. But you should not do this. Use redux to achieve this kind of thing. If it is static data then you can pass this way. But id it is dynamic data then use redux instead.
<Route
path = '/myTab'
component = {(props) => <MyTab {...props} data={data}/>}
/>

React passing props down through switch element

I'm having an issue passing props through React elements (like Switch and Route). In the example below, I would like to pass all props of Dashboard component down to the Account component. Is there a way to achieve this?
App.js
<Dashboard>
<Switch>
// Dashboard props to Account component
<Route path="/account" render={props => <Account {...props} /> } exact />
<Route path="/someothercomponent" component={Someothercomponent} />
</Switch>
</Dashboard>
Dashboard.js
render() {
const children = React.Children.map(this.props.children, child => {
var router = React.cloneElement(child, { image: this.state.image });
return router;
// Like this the router Element does receive the image prop from
// the Dashboard component. Now this image prop needs to be
// passed on to the Account component.
}
I like some of the answers already present. To give you a sense of solving this problem differently and also something to learn and add to your toolbox. I would say use Context. Context provides a way to pass data through the component tree without having to pass props down manually at every level. https://reactjs.org/docs/context.html
So if you get to your Account and have to yet again pass props down this might be a good place to implement this.
When setting up correctly you could do something like this on your page. But again you aren't just passing down one you are passing down all props. And then what if you need to also pass them down on the next component <<< this is the point of Context. I would think using context is better than using a component as your state considering a stateful component is usually limited. With context, your Account component could have several children and you wouldn't have to pass props all the way down to get done what you wish to achieve.
<AppContext.Consumer>
{({prop1, prop2, prop3}) => {
}}
</AppContext.Consumer>
That's assuming you name your variable AppContext when you use React.createContext();
The idea is that passing down props at many levels can be annoying for some but using context you can bring a property in at any time without having to worry about if you passed them down correctly. Be sure to read the article in full there are times where you want to use context and times where you do not.
Yes, use render property instead.
<Route path="path" render={() => <MyComponent {...this.props} />} />
The problem is component is overriding the render props.
Remove component={Account}
I've also added brackets around (props) to improve readability
<Dashboard>
<Switch>
<Route
path="/account"
render={(props) => <Account {...props} /> }
exact
/>
<Route
path="/someothercomponent"
component={SomeOtherComponent}
/>
</Switch>
</Dashboard>
Alternatively:
const renderMergedProps = (component, ...rest) => {
const finalProps = Object.assign({}, ...rest);
return( React.createElement(component, finalProps)
);
}
const PropsRoute = ({ component, ...rest }) => {
return (
<Route {...rest} render={routeProps => {
return renderMergedProps(component, routeProps, rest);
}}/>
);
}
<Router>
<Switch>
<PropsRoute path='/login' component={Login} auth={auth} authenticatedRedirect="/" />
<PropsRoute path='/trades' component={Trades} user={user} />
</Switch>
</Router>
source

React router pass props to route component

I'm trying to pass props from my app.jsx to one of my route components using react router but I get the following error
TypeError: Cannot read property 'acc' of undefined
Here's the code from my app.jsx:
<Route exact path='/FileUpload' acc={this.state.account} ethAdd={this.state.ethAddress} component={FileUpload} />
And the code in the component that route leads to:
constructor(props) {
super(props);
this.setState({
account: this.props.route.acc,
ethAddress: this.props.route.ethAdd
})
}
I don't really understand from reading other solutions on here how this passing of props in react router works, can anyone help me understand what I need to do?
<Route> does not pass custom props to components. Use render function instead:
<Route exact path='/FileUpload' render={
(props) => <FileUpload {...props} acc={this.state.account} ethAdd={this.state.ethAddress} />
} />
As SakoBu mentioned you need to change your constructor:
constructor(props) {
super(props);
this.state = {
account: this.props.acc,
ethAddress: this.props.ethAdd
};
}
Here are few ways you can pass props to a route component.
With the react-router v5, we can create routes by wrapping with a <Route> component, so that we can easily pass props to the desired component like this.
<Route path="/">
<Home name="Sai" />
</Route>
Similarly, you can use the children prop in v5.
<Route path="/" children={ <Home name="Sai" />} />
If you are using react-router v4, you can pass it using the render prop.
<Route path="/" render={() => <Home name="Sai" />} />
(originally posted at https://reactgo.com/react-router-pass-props/)

How to pass props to route without causing component to mount twice?

I am trying to pass props to my components through the accepted way from various sources like this comment
This is my current code
<Router>
...
<Route path="/:id" exact component={() => <GymMain id={params.id} appointmentTypes={appointmentTypeList} />} />
<Route path={`/:id/:name`} component={(props) => {
const { params } = props.match;
const aType = appointmentTypeList.find(at => at.uri === params.name);
return <AppointmentType id={params.id} appointmentType={aType} />
}} />
...
</Router>
However this causes the components to mount twice, once when you navigate into it and then when you navigate away from it (while navigating away old props are passed). This is happening because I am decorating my original component with an anonymous one as explained in this answer .
My Question is how can I prepare the props for child components based on the route params and then pass it on to the routed component. Thanks!
There is a small difference between using component prop vs render prop to render a functional component.
As per the docs:
When you use component (instead of render or children, below) the
router uses React.createElement to create a new React element from
the given component. That means if you provide an inline function to
the component prop, you would create a new component every render.
This results in the existing component unmounting and the new component mounting instead of just updating the existing component.
When using an inline function for inline rendering, use the render or
the children prop (below).
Hence you see the above behaviour in your case. Change your code to use render prop and it would work fine
<Router>
...
<Route path="/:id" exact render={() => <GymMain id={params.id} appointmentTypes={appointmentTypeList} />} />
<Route path={`/:id/:name`} render={(props) => {
const { params } = props.match;
const aType = appointmentTypeList.find(at => at.uri === params.name);
return <AppointmentType id={params.id} appointmentType={aType} />
}} />
...
</Router>
This is not the best way for doing so.
you can use UNSAFE_componentWillUpdate() lifecylcle method to check incoming props.
UNSAFE_componentWillUpdate(nextProps, nextState){
// check for your condition for updating component
if (this.porps !== nextProps) {
this.forceUpdate()
}
else {
return
}
}

Resources