React Router - pass path id to onEnter function - reactjs

I was previously using React Routers URL params to filter an array of content and return the specific content I wanted (filtered by ID). I have now been supplied with a separate API which means I no longer have to return all content and then filter through it, instead I can make one call and return that specific items data.
To make the call on page load I would need to access the id on the Route onEnter function. Is it possible to use a URL param on this onEnter function, and if not - would firing the dispatch function from the container component be the obvious solution?
<Route
path={'locker/my-content/:id'}
component={ManageContentPage}
onEnter={() => { store.dispatch(loadMyContent(// NEED ID HERE)); }}
/>

You can access URL params through onEnter function's argument nextState. Here's an example: React-router - how to use route params in onEnter?

If you're using react-router 4+, then the onEnter prop no long exists, and you'll have to use the render prop to achieve similar functionality.
For react-router < 4
Any function passed to onEnter will take nextState as its argument. This state object will contain your params
So to access this value, you'd need something like the following:
<Route
path={'locker/my-content/:id'}
component={ManageContentPage}
onEnter={(nextState) => { store.dispatch(loadMyContent(nextState.params.id)); }}
/>

Related

How to send MULTIPLE params with useHistory hook in React?

history.push({pathname: '/search?', state: {param1: 'value1'}}) this doesn't work. It just redirects to /search
history.push('/search?', {param1: 'value1'}) this doesn't work.
history.push('/search?', ['param1=value1']) this doesn't work.
history.push('/search?', [... 'param1=value1']) this doesn't work.
history.push('/search?', state: {param1: 'value1'}) this doesn't work.
The only thing that works is this: history.push('/search?param1=value1').
But I need to dynamically send multiple params. How do I do that? The official documentation shows the first example with an object, but it's not working for me. I am using functional components by the way.
If I understand your question correctly you want to dynamically get/set the queryString parameters of the URL.
None of the examples you've shared work because the second argument to history.push is route state.
history.push(path|To, [state])
Trying to pass queryString parameters in the second argument doesn't work.
Use URLSearchParams to construct a searchParams object that you can then update the params of, to be used in the history.push method. This method persists any existing queryString parameters.
Example:
const { search } = useLocation();
...
// get the search params for the current location
const searchParams = new URLSearchParams(search);
// update params as necessary, i.e. set/delete
searchParams.set("param1", "value1");
searchParams.set("param2", "value2");
searchParams.set("param3", "value3");
// push new route using to object
history.push({
pathname: "/search",
search: searchParams.toString(),
});
OFC, if you don't need this level of control and just need to inject the three dynamic values you could just use a template string.
history.push(`/search?param1=${value1}&param2=${value2}&param3=${value3}`);
How about this?
history.push('/search?param1=value1&param2=value2')

React Router: Query Param Match?

According to the accepted answer to this question, React Router 4 doesn't match query parameters anymore. If I go from a URL matched by one of my <Route>s to the same URL with a different query string, the content doesn't seem to change. I believe this is because navigating between URLs that match the same <Route> doesn't change the content, but please correct me if I'm wrong. Given this, how do I use React Router for a set of URL's that need to differ only by query parameter?
For example, many search engines and other sites that use search bars, including the site I am working on, use a query parameter, commonly q or query. The user may search for one thing, then decide that is not what he/she wants and search for another thing. The user may type in the second URL or search with the search bar again. There isn't really a place for the search term in the URL path, so it kind of needs to go in the query string. How do we handle this situation?
Is there a way, with React Router, to link to a URL that only differs in the query string and change the content, without refreshing the entire page? Preferably, this wouldn't require any external library besides React and React Router.
Try the render function prop instead of component prop of Route. Something like this:
<Route render={props => {
// look for some param in the query string...
const useComponentA = queryStringContains('A');
if(useComponentA) {
return <ComponentA {...props}/>;
} else {
return <ComponentB {...props}/>;
}
}}/>
There are 2 ways to do that:
1) Use location.search in react component to get the query string, then pass it to child component to prevent re-rendering the whole component. React-router has the official example about this.
2) Define a regex path of router to catch the query string, then pass it to react component. Take pagination as an example:
routes.js, for router config you can refer this
const routerConfig = [
{
path: '/foo',
component: 'Foo',
},
{
path: '/student/listing:pageNumber(\\?page=.*)?',
component: 'Student'
},
Student.js
render() {
// get the page number from react router's match params
let currentPageNumber = 1;
// Defensive checking, if the query param is missing, use default number.
if (this.props.match.params.pageNumber) {
// the match param will return the whole query string,
// so we can get the number from the string before using it.
currentPageNumber = this.props.match.params.pageNumber.split('?page=').pop();
}
return <div>
student listing content ...
<Pagination pageNumber = {currentPageNumber}>
</div>
}
Pagination.js
render() {
return <div> current page number is {this.props.pageNumber} </div>
}
The 2nd solution is longer but more flexible. One of the use cases is server sider rendering:
Apart from the react components, the rest of the application (e.g. preloaded saga) need to know the url including query string to make API call.

How to use onClick event on Link: ReactJS?

In React router, I have to and onClick attributes as shown below
<li key={i}><Link to="/about" onClick={() => props.selectName(name)}>{name}</Link></li>
state = {
selectedName: ''
};
selectName = (name) => {
setTimeout(function(){this.setState({selectedName:name});}.bind(this),1000);
// this.setState({selectedName: name});
}
to attribute navigates to about Route
onClick assigns value to state variable selectedName which will be displayed when navigated to About page.
When I give timeout insided function called on click, its navigating to new page and after sometime state is getting updated resulting in displaying previous name until state is updated with new name.
Is there a way where it will navigate to the new route only after the code in onClick function gets executed.
You can get the entire code [here].(https://github.com/pushkalb123/basic-react-router/blob/master/src/App.js)
One possible way is, Instead of using Link, use history.push to change the route dynamically. To achieve that remove the Link component and define the onClick event on li. Now first perform all the task inside onClick function and at the end use history.push to change the route means to navigate on other page.
In your case change the router inside setState callback function to ensure that it will happen only after state change.
Write it like this:
<li key={i} onClick={() => props.selectName(name)}> {name} </li>
selectName = (name) => {
this.setState({ selectedName:name }, () => {
this.props.history.push('about');
});
}
Check this answers for:
When to use setState callback
How to navigate dynamically using react router dom
Alternatively, I would recommend using URL Params in order to capture the name of the person that the about page is about. Thus, instead of the url being /about and the name being behind the scenes, it would be /about/tom or /about/pushkal. The way that you do this is by defining params in the URL router as such in your index.js:
<Route path="/about/:name" component={AboutPage}>
Now, when you link to the about page, you would do it as such:
<Link to={"/about/" + name}>{name}</Link>
Now, in your AboutPage component, you can access the name param as a prop in this.props.params.name. You can look at more examples here.
This method is a bit different than your current approach but I suspect it will lead to easier design later on

React Router: How to render different Components with different parameters

Is it possible to have routes with different parameters and render different components based on the parameters?
For an example can I do:
<Route path="/SamePath/:param1" component={Component1} />
<Route path="/SamePath/:param2" component={Component2} />
If you are trying to have a the same route structure but render different components based on the value of the param, then you could try using render to decide which component to render based on the value of the param:
<Route path="/SamePath/:param1" render={ (props) => {
if (props.match.params.param1 == 'something') {
return <Component1 { ...props } />
} else {
return <Component2 { ...props } />
}
} />
If it is for lets say only two routes that will differ instead of using the capturing : just type in the name Eg.( samePath/organges samePath/apples) of the route if thats not the case and you want to capture a variarty range of to render differnt components you could use the regex routes feature of react router 4.. but that is much more complicated
In my project, I created a dictionary mapping possible values of the parameter to the desired component, and created <Route>s based on this dictionary.
<Switch>{
[{
['param1']: Component1,
['param2']: Component2,
}].forEach((component, param) =>
<Route
component={component}
exact
path={`/path/with/${param}`}
/>
)
}</Switch>
This will produces individual <Route>s for each path variation you wish to match. It will render different components for each route and, to me, seems in keeping with React-Router v4's dynamic routing ethos.
I encountered a problem: I couldn't access the value of the param via the match property passed to the rendered child. The Route matches against a specific path string (e.g. '/path/with/paramValue1) and not against a route descriptor with a parameter (e.g. '/path/with/:param'). This means thematch.params` dictionary passed to the child will not contain any keys or values. If you don't need the params in your child, this method will work fine. I wanted to inspect the param in the child so I had to look for a different method.

Navigate to a different route programmatically

I have a usecase where I have a list of elements inside some component similar to vanilla html select. I want, when user clicks on any of the element inside list, route should change. Can I use some regex while defining route and change route by using some API method from react router, onValueChange event of select?
Example
Root route: /
List Data: Cat, Dog, Elephant
When someone clicks on Cat I want him to get navigated to /Cat, similarly /Dog, /Elephant.
So after talking about it for a bit. figured out what you were wanting to do.
you need to define a new route in react-router that you can use for this history change.. something like this:
<Route exact path="/:name" component={SomeComponent} />
in your component you have access to this "name" via this.props.params.name
you can also call an action to update the store before updating this route
Note: if you stay with v2... You should make your actions return a promise, so that you can just chain the event in your component. aka:
onChange={this.handleChange}
handleChange = (value) => { // assuming you have the value here.. it may be e is the function param and you get the value as e.target.value
this.props.saveSelectedAnimal(value).then( () => {
this.props.history.push("/");
})
}

Resources