How to pass multiple functional components in react route? - reactjs

I have something like this,
<Switch>
<Route path="/playground/:id">
<Header></Header>
<Playground></Playground>
<Footer></Footer>
</Route>
<Switch/>
I want to pass all the functional components through the component props like this,
<Route path="/playground/:id" component={Playground}></Route>
But here I am only able to pass one component through the component props. How could I pass them all?
I want to do that because while passing the functional component through the component props, I can get the match.params.id value but when I pass the functional components by the first way, I cannot get the value of match.params.id and simply gives the error. `params undefined
So, the first question, How to pass multiple components?
And second question, Why I cannot access value of from match.params.id and how can I access although if I use the first way to pass the components?

1.You can stuff all all the elements into one component and transfer it to the router
2.To take some kind of value from the params one of the ways is to use the react-router-dom can be taken like this through the useParams hook in as stated in below
const { id } = useParams()
console.log(id)
or
const query = useParams()
console.log(query.id)

Related

React router two URL parameters &more

I'm using react-router-dom and this is currently how it looks:
<Switch>
<Route path={"/layouts/:layoutID"} component={Layouts} />
<Route
path={"/dashboard/:dashboardID"}
component={Dashboards}
/>
</Switch>
When the user navigates to "/dashboard/:dashboardID" inside this component he can choose a sub-page onClick, and I want the URL structure will be "/dashboard/:dashboardID/:pageID" pageID will navigate to 'PageIdComponent' this component will get a 'match' props and will show pageID
Please see the attached file that shows the necessary structure.
What is the best way to implement it?
If you have the child route inside your Dashboard component, then you can use the match.path value to build up a sub route:
<Route path={`${props.match.path}/:dashboardId`}>} component={SubDashboard} />
in order to make the path match what the parent's had and then add a new variable on top of that.
Then you can navigate to it using the match.url to make a link to the current URL and add the subPage to it:
<Link to={`${props.match.url}/pageFour`}>Page four</Link>
Instead of prop driling, React Router offers ways to get the relevant props to whatever components you desire via withRouter HOC or various hooks. The hooks were introduced in version 5.1
To get the match via a hook you can use const match = useRouteMatch()
To get the relevant props via the HOC:
const Example = useRouteMatch(function Example({ match, location, history }) {
return <div>{match.url}</div>;
});
For react router v6 (that's upcoming):
https://reacttraining.com/blog/react-router-v6-pre/#nested-routes-and-layouts

Can useParams be able to use inside App.js?

Good day, hoping that I am not bothering you guys.
Details
I have a path like this
localhost:3000/Project-1/todo-1
and Switch of Route like this
<Route path='/:project/:todo' component={Todo} />
Expected Output
When browser is in the example path, what I expected is App.js can also get params object by using useParams but shows empty. Did I misuse the hook? Thank you for the answers.
Additional Details
I did use useLocation that returns something like path but that is not my aim, it should be something like
params: {
project: 'Project-1',
todo: 'todo-1 '
}
that is returned using useParams for ease of extraction of project and todo values
I believe Route injects props to components it renders, so you should be able to pull it straight from the match prop within Todo: this.props.match.params.
You’ll have access to match objects in various places:
Route component as this.props.match
Route render as ({ match }) => ()
Route children as ({ match }) => ()
withRouter as this.props.match
matchPath as the return value
https://reacttraining.com/react-router/web/api/match
This only works for components rendered within a router on a route, i.e. the component a Route is rendering, or a child component further down the DOM tree. If App.js is rendering the router it won't have it.
If you need to do something with route params in the root App.js you can wrap it in a route, with unspecified path so it matches all routes, in your router using the render prop. Just ensure this is rendered outside any Switch components though as switches only return the first match, not all matches.
<Route
render={({ match }) => {
console.log('match', match);
// do what you need with the `match` prop, i.e. `match.params`
return null; // return null to indicate nothing should actually be rendered
}}
/>

Passing data from the Redux store, as Props, to an unknown number of routes

Here is the situation:
I am getting an unknown amount of data from a service and putting it in my Redux store
Each data-point will have its own route /:rid
The route is setup like this <Route path=':rid' component={Restaurant}/> using react-router (v3) -- see full render method below
I'd like to be able to pass the correspond data about the specific restaurant, the one whose route was navigated to, to the Restaurant component as props so it can render the component with the correct information for that restaurant
Right now my solution is to pass all the restaurants from the store into the Restaurant component as props this.props.restaurants. Then in componentWillReceiveProps I loop over all the restaurants in this.props.restaurants and check to see if the rid of each restaurant is equal to the this.props.routeParams ie :rid. If it is I then set that state to contain the data I want to show and reference this.state.name as opposed to the data being on `this.props.name'
Is there another way to do this? Right now it's not a performance issue but I can imagine looping over and arbitrarily large data set could lead to so serious load times. Also, it just seems like there should be a way for react-router to pass in this data as props so I can keep this component stateless.
Ideally, something like this would happen:
a request is made to /1234
react-router in my index.js consults/queries the redux store and finds the data for the restaurant with rid 1234 and passes it as props to the component it renders
I imagine it looking something like this <Route path=':rid' component={<Restaurant {...matchedRestaurant} />}/>
Perhaps this questioning can be asked in short like, how do I make a unknown number of routes such that when one is navigated to it is rendered with the data for that corresponding restaurant as props?
Restaurant.js:
componentWillReceiveProps(nextProps) {
this.props.restaurants.forEach((restaurant) => {
if(restaurant.rid == nextProps.routeParams.rid) this.setState({name: restaurant.name})
})
}
index.js:
render(
(
<Provider store={store}>
<Router history={hashHistory}>
<Route path='/' component={App}>
<IndexRoute component={RestaurantList} />
<Route path=':rid' component={Restaurant}/>
</Route>
</Router>
</Provider>
),
document.getElementById('root')
)
https://github.com/caseysiebel/corner-team/blob/master/src/index.js
Instead of having react-router figure this out for you, you should be using selectors (and potentially a package like reselect). Reselect even has a section on how to base your selector on props (in this case like the routerParams.rid): https://github.com/reactjs/reselect#accessing-react-props-in-selectors
For the non-Reselect solution, you could simply change the connect in your Restaurant component like so:
#connect((state, props) => {
return {
restaurants: state.restaurant.restaurants.find((restaurant) => {
return restaurant.rid == props.routeParams.rid
}),
}
})
As #Sean Kwon commented, you should also normalize your data which would make this selector trivial:
#connect((state, props) => {
return {
restaurants: state.restaurant.restaurants[props.routeParams.rid],
}
})
Assuming you have connected your action via mapDispatchToProps, you organize your store and async actions so that this can be possible.
componentDidMount() {
this.props.fetchRestaurant(this.props.params.rid)
}
The store will then update your component with the corresponding restaurant data. This way, you're calling some kind of action to get the corresponding data whilst reducing the need to use the component state, which you should try to avoid in order to keep your state centralized.
Otherwise, for a quick and dirty solution, you can just do this really quickly.
componentWillReceiveProps(nextProps) {
var name = this.props.restaurant.find(res => res.rid === nextProps.routeParams.rid)
this.setState({name: name})
}

How can i get :params from location?

I would like to implement wide spread pattern mywebsite/en/welcome. In this case i have to modify all my for including actual param :lang into their prop 'to'. Without this implementation i couldn't know current language. So, i use handleLocationChange to spread curent :lang to all my components. But i receive location prop which include only current path and query. How can i receive curent :lang param too ?
p.s. i would like to avoid all of this but AFAIK react-router doesn't support relative links
When a <Route>'s component is rendered, it is passed a params prop which you can use to access the lang parsed from the URL. If you need access to this from a child component you will have to pass it as a prop to that component.
<Route path=':lang/my-page' component={MyPage} />
const MyPage = (props) => {
const { lang } = props.params
return (
<div>This page's language is: {lang} </div>
)
}

Get props (or global variables)

I'm attempting to set some global variables on my app which I want to be available to all components. Lets say for example that I want a 'language' and a 'status' property to be passed to each component. This property won't be rendered to the page, instead it will be added to the props for each component, this will be so I can check for that variable when each component loads and output the appropriate styles and languages.
I was hoping it would be something simple like adding props to the router, however no matter what I try, the props come back as 'undefined' on my child components (only the main layoutWrapper component gets the props). Here is how it looks so far:
//app.js
var LayoutWrapper = React.createClass({
render: function () {
return (
<Layout status="available" />
);
}
});
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" language="en-gb" component={LayoutWrapper}>
<IndexRoute component={Index}></IndexRoute>
</Route>
</Router>,
app);
When handling global level state data, it's recommended you use some kind of state framework like Flux. I'd recommend Redux as it does a great job of reducing boilerplate code to easily pass down app state to any connected component (and subsequently subscribe these components to any changes in the state).
What you are doing fails because there's no consistent way of creating "global" props; you could use the context variable but this is an unstable feature that is not recommended for production use. Otherwise, you have to manually pass down your props from parent to child explicitly.

Resources