How do I pass props down from react-router-doms's render? - javascript

I am aware that to pass down props to my Routes I need to use the render. For example:
const BaseRouter = (props) => (
<div>
{console.log(props)} // this displays everything I need, including props.username
<Route exact path='/room/' render={(props) => <Room {...props} />} />
</div>
);
The problem is that inside my Room component, the only props passed down are history, location, and match (these come straight from react-router-dom).
I have also tried manually passing the props needed down:
<Route exact path='/room/' render={(props) => <Room {...props} username={props.username} />} />
But inside Room, I still get undefined for props.username. Any ideas?

You need to give a different name to you variables. Doing render={(props) => <Room {...props} /> will always pass the router props, that is why props.username is undefined.
const BaseRouter = (props) => (
<div>
<Route exact path='/room/' render={(routeProps) => <Room {...routeProps} {...props} />} />
</div>
);

Route component takes children as well.
const BaseRouter = (props) => (
<div>
{console.log(props)} // this displays everything I need, including props.username
<Route exact path='/room/'>
<Room {...props}/>
</Route>
</div>
);
In Room component you can use useHistory, useLocation and useRouteMatch hooks. If Room is class based component then withRouter HOC is also useful.

Related

React pass information to child of Route

I am building a react navigation and have to pass info to the sidenav. Therefore I have to pass props to my body, but the body is rendered by a router and I can't just pass them. I have seen a lot on this, but always with self-closing tags, so that you can just paste the props in the render function of the Route. Below the code:
App.js
function App() {
//...
return (
<Router>
<Switch>
{Object.keys(routesGroupedByLayouts).map(layoutName => {
const LayoutTag = layouts[layoutName];
const layoutRoutes = routesGroupedByLayouts[layoutName].map(route => route.path);
return <Route key={`layout-${layoutName}`} exact={true} path={layoutRoutes} render={() => {
return (
<ScrollToTop>
<LayoutTag>
<h1>sdsdddscxcxs</h1>
<Switch>
{routesGroupedByLayouts[layoutName].map(route => (
<Route key={route.path}
path={route.path}
exact={route.exact === true}
render={() => <route.component fallback={<Loading />} />} />
))}
</Switch>
</LayoutTag>
</ScrollToTop>
)
}} />
})}
</Switch>
</Router>
);
}
default.jsx
const Default = ({children}) => {
//...
return (
<Layout>
<Layout.Body id={"main-content"}>
<div>
{children}
</div>
</Layout.Body>
</Layout>
)
}
I hope I pasted everything needed here. So the LayoutTag passes the children to the default.jsx, but I don't know how to pass the children to the rendered Body of the Route element. I have to pass the body a function that executes in default.
Hope somebody can help me out, thanks in advance!

React HOC with Context - should not use <Route render> and <Route children> in the same route

So I am running into the infamous React Router v4 warning:
Warning: You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored
This happens after wrapping my component, which itself renders a <Route /> inside my HOC which provides my React context.
This all looks a bit like this:
App.js:
<myContextProvider>
<Router>
<Switch>
<myLayout path="/foo" Component={someComponent}>
...
</Switch>
</Router>
</myContextProvider>
my stores.js file includes the following:
export const withMainStore = (Component) => {
return (props) => {
return (
<MainConsumer>
{store => (
<Component {...store} {...props} />
)}
</MainConsumer>
)
}
}
myLayout.js:
class myLayout extends React.Component {
const { Component, ...rest } = this.props;
// ...
render() {
return (
<Route {...rest} render={(props) => (
<Component {...rest} {...props} />
)}/>
)
}
}
export default withMainStore(myLayout)
I hope my whole situation here is somewhat clear. can someone explain to me why the warning occurs? At NO point in my whole app, I have something like: <Route>...</Route> - I am always using <Route {...rest} render={(props) => ( ... )} />!
So I got no idea why this is happening. I has to have something todo with my withMainStore HOC, because when I remove it the error is gone. but still I do not understand why this causes the error.

How to pass props to nested routes in React Router 4?

I have a component tree like this:
-App
--UserList
---UserItem
---UserItem
---UserItem
--User
I'm not being able to pass user data from UserItem to User. This is what I have:
App.js
export default class App extends Component {
state = { users: [] }
componentDidMount() {// fetch and setState}
render() {
return (
<BrowserRouter>
<Route
exact
path="/"
render={() => <UserList users={this.state.users} />}
/>
</BrowserRouter>
)
}
}
UserList.js
export default function({ users }) {
return (
<div>
{users.map(user => (
<UserItem user={user} key={`${user.id}`} />
))}
</div>
)
}
This is where the problem is: I want to pass the data from the parent component to the child User component, instead of having to fetch the user data from the API again.
UserItem.js
export default function({ user }) {
return (
<div>
<Link to="/user">{user.name}</Link>
<Route path={`/user/${user.name}`} render={() => <User user={user} />} />
</div>
)
}
I'm not sure what you're trying to implement here. Your app renders the UserList when then route is /. The UserList renders a UserItem component for each user in the array. Each UserItem simply renders a route specific to every user, which will render the User component if that route is triggered.
But if I'm not mistaken, the UserList will not be rendered if the route is anything but /, so if someone accesses user/..., the inner routes won't actually exist.
Essentially, this app will not render anything.
If you remove the exact keyword from the route in App, I think you'll get the result you are looking for. In this case, opening /user/<USER_NAME> will render the User element for that user.
Your question is regarding passing props into a component through a route, and the mechanism you've used is correct.
<Route path={...} render={() => <User user={user} />} />
This is actually right. See the code linked below. On changing the route to /user/User1, you'll see the name of "User1" rendered in the app.
See the working code here: https://codesandbox.io/s/18w3393767
You should use this.props.users in the UserItem component
i'm not sure but could you pass props like below, here i pass props to render and then to User Component
<Route path={`/user/${user.name}`} render={(props) => <User user={user} {...props} />} />
export default function({ users }) {
return (
<div>
{ this.props.users.map(user => (
//mistake here this.props.users.map not users.map
<UserItem user={user} key={`${user.id}`} />
))}
</div>
)
}

How to pass props between React Routes 4

I can't find the way how to send object from one React Route Component to another.
For example I have container router like this:
const App = () => (
<Router>
<div>
<Route exact path="/" component={Home} />
<Route path="/sections/:id"
component={Section} />
</div>
</Router>
)
with Home component like this:
const Home = () => {
const Sections = tilesData.map( (section, index) => (
<div>
<img src={section.img} height="200" /><br/>
<Link to={`/sections/'${section.id}`} >Details for {section.author}</Link>
<hr/>
</div>
))
return(
<div>
{Sections}
</div>
)
}
and I don't understand =\ how to pass selected object when next route clicked with <Link>. Here is example component:
const Section = (props) => {
return(
<div>
Section {props.title}
<img src={props.img} />
</div>
)
}
Here is code example: https://codesandbox.io/s/Mv037AE3
In react-router v4, we usually do the following to pass in a ProductPage component:
<Route path='/' component={ProductPage} />
When you want to use props, you can prepare a function that returns your component with the desired props. Here I'm preparing to pass in a toggleSidebarOn prop:
const MyProductPage = (props) => {
return (
<ProductPage
toggleSidebarOn={this.toggleSidebarOn.bind(this)}
{...props}
/>
);
}
Then you use the render prop of the <Route /> instead of its component prop. Do not use both and do not use the component prop as this leads to undesired remounting and might break your app (e.g. for me CSS transitions for the sidebar animation stopped working properly).
Using the render prop for the MyProductPage we can then pass in our toggleSidebarOn prop to its target route:
<Router>
<div>
<Switch>
<Route exact path="/products" render={MyProductPage} />
<Route exact path="/perspectives" component={PerspectivePage}/>
<Route component={NotFound}/>
</Switch>
</div>
</Router>
Hope this helps!

Passing props to component in React Router 4

I am new to react-router and I just started writing an app using react-router V4. I would like to to pass props to components rendered by <Match /> and I am wondering about what's the 'best' or 'proper' way to do so.
Is it by doing something like this?
<Match pattern="/" render={
(defaultProps) => <MyComponent myProp = {myProp} {...defaultProps} />
}/>
Is this (passing props to components to be rendered by <Match />) even a good practice to do so with react-router or is it an antipattern or something; and if so, why?
You must use the render prop instead of component to pass on custom props, otherwise only default Route props are passed ({match, location, history}).
I pass my props to the Router and child components like so.
class App extends Component {
render() {
const {another} = this.props
return <Routes myVariable={2} myBool another={another}/>
}
}
const Routes = (props) =>
<Switch>
<Route path="/public" render={ (routeProps) =>
<Public routeProps={routeProps} {...props}/>
}/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/" render={ (routeProps) =>
...
}/>
</Switch>
render() {
return (
<Router history={browserHistory}>
<Switch>
<Route path="/"
render={ () => <Header
title={"I am Title"}
status={"Here is my status"}
/> }
/>
<Route path="/audience" component={Audience}/>
<Route path="/speaker" component={Speaker}/>
</Switch>
</Router>
)
}
I'm fairly new to react-router and came across a similar issue. I've created a wrapper based on the Documentation and that seems to work.
// Wrap Component Routes
function RouteWrapper(props) {
const {component: Component, ...opts } = props
return (
<Route {...opts} render={props => (
<Component {...props} {...opts}/>
)}/>
)
}
<RouteWrapper path='/' exact loggedIn anotherValue='blah' component={MyComponent} />
So far so good
I use render in combination with a defined method like so:
class App extends React.Component {
childRoute (ChildComponent, match) {
return <ChildComponent {...this.props} {...match} />
}
render () {
<Match pattern='/' render={this.childRoute.bind(this, MyComponent)} />
}
}
The render prop is meant for writing inline matches, so your example is the ideal way to pass extra props.
I'll do it like the following to improve clarity.
const myComponent = <MyComponent myProp={myProp} {...defaultProps} />
<Match pattern="/" component={myComponent} />
By this your router code won't get messed up!.

Categories