Call React component with dynamic URL parameters - reactjs

For now, I am rendering a React Component with a static URL.
I would like to pass it a dynamic URL.
For instance, rather than calling it like this:
https://www.example.com
I would like to call it like this:
https://www.example.com/?name=John
And I would like this to update its this.props.name or this.state.name components.
Can I do that?
What should I use for this purpose? react-router?
Or can this only be done from the backend?

You do not need react-router. Following would parse your url:
let url_parameter = {};
const currLocation = window.location.href,
parArr = currLocation.split("?")[1].split("&");
for (let i = 0; i < parArr.length; i++) {
const parr = parArr[i].split("=");
url_parameter[parr[0]] = parr[1];
}
However, if you use react-router you might want to go the the resource. Than the link would look like this:
http://www.example.com/john
In this case react allows to get the resource in the this.props.params:
Here how to specify the route:
<Route path="/:name" component={Name}>
Here how to access the name in the component 'Name':
....
render() {
const {name} = this.props.params;
...
}
I hope this helps.

Related

Get multiple URL parameters using useParams() hook

I am trying to pass multiple parameters in a url using React-Router 5.1 and the useParams hook in a Functional component.
However I encounter really strange behavior.
I paste the url into the client.
Url:
http://localhost:3000/onboarding/eventId=5e9a173f9166f800128c04d1&groupId=5e9a173f9166f800128c04dc&userId=5e9a173f9166f800128c04da
Path:
<Route path = '/onboarding/:eventId?/:groupId?/:userId?' exact component = {OnboardingViewController} />
Strange thing #1:
I have to make the params optional, or the browser just hangs forever.
I fish them out using all these strategies:
var { eventId, groupId, userId } = useParams();
var { eventId } = useParams();
var { groupId } = useParams();
var { userId } = useParams();
Strange thing #2:
Unfortunately when trying to use these params this happens:
{userId: undefined, eventId: "eventId=5e9a173f9166f800128c04d1&groupId=5e9a173f9166f800128c04dc&userId=5e9a173f9166f800128c04da", groupId: undefined}
The hook just takes the first param and interprets the rest a part of te first.
Strange thing #3:
Since adding this url params query accessing the page laoding is extremely slow, multiple seconds.
What am I not seeing, doing wrong?
ANSWER:
What I was doing wrong:
I was using url/eventId=123.
This is wrong.
You just need to supply the resource at the right place in the URL url/1/2/3.
correct:
http://localhost:3000/onboarding/5e9aaf4fc27583001190834e/5e9aaf60c275830011908361/5e9aaf4fc275830011908357
You then tell the Router that those things will be called eventId & groupId & userId.
<Route path = '/onboarding/:eventId/:groupId/:userId' exact component = {OnboardingViewController} />
Then you can access these in the component using the userPrams() hook.
var { eventId, groupId, userId } = useParams();
Thanks everyone!
Your Route structure and Route doesn't match
If you want to use params in your URL would be
http://localhost:3000/onboarding/5e9a173f9166f800128c04d1/5e9a173f9166f800128c04dc/5e9a173f9166f800128c04da
And your Route component would be:
<Route path = '/onboarding/:eventId/:groupId/:userId' exact component = {OnboardingViewController} />
And then you can use this in the OnboardingViewControllercomponent:
var { eventId, groupId, userId } = useParams();
console.log(eventId,groupId,userId)
You are mixing up match parameters with URL query parameters.
The URL query parameters can be retrieved from the location object using the useLocation hook.
Given URL http://localhost:3000/onboarding/?eventId=5e9a173f9166f800128c04d1&groupId=5e9a173f9166f800128c04dc&userId=5e9a173f9166f800128c04da
{
pathname: '/onboarding/',
search: '?eventId=5e9a173f9166f800128c04d1&groupId=5e9a173f9166f800128c04dc&userId=5e9a173f9166f800128c04da'
}
would need a route path="/onboarding/" though
You can use a QueryParameter processing library to then convert these to a map object.
If you could massage your URL to be in the form:
http://localhost:3000/onboarding/5e9a173f9166f800128c04d1/5e9a173f9166f800128c04dc/5e9a173f9166f800128c04da
Then the route path='/onboarding/:eventId/:groupId/:userId' can then match the path params returned by useParams.
const { eventId, groupId, userId } = useParams();

How to create dynamic route in gatsby

I have setup gatsby project using this link. It is working correctly.
Now I know how to create route by defining the component inside the pages folder. But now I have a new challenge I need to create one dynamic route so that I can pass my id in it (Just like reactjs).
<Route path: "/path/:id"/>
How do I do that in gatsby?
You have to explicitly tell gatsby that a path should be dynamic. From the docs:
// gatsby-node.js
// Implement the Gatsby API “onCreatePage”. This is
// called after every page is created.
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
// page.matchPath is a special key that's used for matching pages
// only on the client.
if (page.path.match(/^\/app/)) {
page.matchPath = "/app/*"
// Update the page.
createPage(page)
}
}
and then you can use dynamic routing in src/pages/app.js
import { Router } from "#reach/router"
const SomeSubPage = props => {
return <div>Hi from SubPage with id: {props.id}</div>
}
const App = () => (
<Layout>
<Link to="/app/1">First item</Link>{" "}
<Link to="/app/2">Second item</Link>{" "}
<Router>
// ...dynamic routes here
<SomeSubPage path="/app/:id" />
</Router>
</Layout>
)
export default App
Everything that goes to /app/* will be handled dynamically now. You should find your id as usual in the props.
Have a look at their authentication example https://github.com/gatsbyjs/gatsby/tree/master/examples/simple-auth
You can use square brackets ([ ]) in the file path to mark any dynamic segments of the URL. For example, in order to edit a user, you might want a route like /user/:id to fetch the data for whatever id is passed into the URL.
src/pages/users/[id].js will generate a route like /users/:id
src/pages/users/[id]/group/[groupId].js will generate a route like /users/:id/group/:groupId
Reference: https://www.gatsbyjs.com/docs/reference/routing/file-system-route-api#creating-client-only-routes
You can use gatsby-plugin-create-client-paths. It uses matchPath. For more info check
https://www.gatsbyjs.org/docs/gatsby-internals-terminology/#matchpath
https://www.gatsbyjs.org/packages/gatsby-plugin-create-client-paths/
This answer is Super late, but for anyone in the future who is faced with this problem, I have a simpler solution.
In Gatsby terms it's called a Splat Route.
For examples, If you want some page "domain.com/profile/[id]", where id can be any number, which will be used to display different data inside the website, you should name your page as [...id].
Now inside the page you can access this id as
const ProfilePage = (props) => <div>This page is for id number {props.params.id}</div>
Note: Don't miss the 3 dots, that is what signifies a splat route in gatsby.

How to do multiple optional params with react-router?

I am using "react": "^16.8.6" and "react-router": "^5.0.0"
I am working on a search page.
And the page should have some filter condition
and the url look like this in regular way:
/Search?SearchText=bachelor&State=New+South+Wales&CourseLevel=Diploma&CourseArea=Agriculture
how do I make it in react-router way?
I was think about I can do it in this way, because the params is optional,
so the route could be
/search/:CourseArea?/:State?/:CourseLevel?/:StudyMode?/:SearchText?
but how do I know which query is passed in url, and which not?
Thank in Advance
Simply use query-string to parse the argument and leave your route just as is:
import queryString from 'query-string';
class SearchComponent extends React.Component{
render(){
// url is 'https://www.example.com/search?id=123&type=4';
let url = this.props.location.search;
let params = queryString.parse(url);
console.log(params);
// The result will be like below
// { id: 123, type: 4 }
// other code
}
}

React Router: Handling queries through React router

So, currently, I have a routing component:
<Route path="/lists/:query" component={Lists} />
I get a call like:
http://localhost:4567/lists/page=17&city_codes=2567
In my Lists component, I handle this query in this way:
componentDidMount() {
const query = match.params.query;
const cleanQueryString = query.replace(/[|;$%#"<>()+,]/g, '');
// break up the string using '&' and '=' into an object
const properties = this.queryURL(cleanQueryString);
const cleanQueryObj = _.pick(Object.assign({}, ...properties), [
'page',
'city_codes',
'min_monthly_fee',
'max_monthly_fee',
'order_by',
]);
// update the query object based on component state
this.setState({ query: cleanQueryObj }, () => {
cleanQueryObj.page && this.updateIndex(parseInt(cleanQueryObj.page, 10));
});
// call axios request and update redux
dispatch(handleLists(cleanQueryObj));
// update browser url
this.props.history.push(cleanQueryObj);
Now, I see a lot of major sites using ?q= before the query and I'm wondering what I'm missing or what could be improved?
Thoughts?
While what you are doing is technically valid, it is a bit non-standard. The way you use the router :query param and the way it is formatted, reaaaaly looks like an actual location.search parameter format, and not a path parameter.
A more standard way to do it, would be with the following URL:
http://localhost:4567/lists?page=17&city_codes=2567
And code as follow:
// In your routes, just a simple route with no path params
<Route path="/lists" component={Lists} />
// In your component
import queryString from 'query-string'
[...]
componentDidMount() {
// Use location object from react-router
const { search } = this.props.location
// Parse it using a npm dedicated module
const { page, city_codes } = queryString.parse(search)
// Now you can use those params
]);
Edit: and now an actual answer to the question:
?q=blah is usually used in a search context, with q parameter being a string used to search something. There can be other parameters following for example ?q=blah&ext=txt.
It is hence different from your :query path param, which is encoded to contain multiple parameters, while q here is a single ready-to-use parameter.

Programmatically navigate using react router without duplicating paths

I've read this post, but I don't like having browserHistory.push('/some/path') in one of my components while I have <Route path="/some/path" component={SomePage} /> in my router file since the path is duplicated. What if I want to change that path to /another/path? Now I need to remember to update it in the router file and also my component.
Is there a better way around this? I was thinking that I could have "/some/path" and all my other paths defined in some constants file that gets imported and referenced in my router and my component. Example:
paths.js
var Paths = {
myPath: "/some/path",
...
}
module.exports = Paths
router.jsx
var Paths = require('constants/paths');
...
<Route path={Paths.myPath} component={SomePage} />
component.jsx
var Paths = require('constants/paths');
...
browserhistory.push(Paths.myPath)
This seems like it could get a little messy when dealing with URL parameters like /some/path/:id, so I was hoping there might be a better way.
This is what I have done in the past for routing to make it simpler / more streamlined.
(as a side note i used lodash here so if you aren't you can use native functions to do basically the same thing. lodash just adds a bunch of nice features / functions that you dont need to go write yourself)
in my routes.jsx file you should create functions that convert any parameters into a url with a default path for this answer lets just make one for a profile route
export function pathToProfile(userName, params) {
return path(Paths.PROFILE, _.assign({userName}, params));
}
the path() function is just a simple helper utility function for generating a path.
path(url, params, urlMap) {
if(!url) {
console.error("URL is not defined for action: ", params);
}
if(!params)
return url;
params = _.merge({}, params);
if(urlMap) {
_.keys(urlMap).forEach((k) => {
params[urlMap[k]] = params[k];
delete params[k];
});
}
if(url.indexOf(":") !== -1) {
url.match(/:([0-9_a-z]+)/gi).forEach((match)=>{
var key = match.replace(":", "");
url = url.replace(match, params[key]);
params = _.omit(params, key);
});
}
if(_.keys(params).length > 0) {
url = url + "?" + this.paramsToString(params);
}
return url;
}
now looking at the constants file
Paths {
PROFILE: '/user/:username',
}
Finally usage.
I wouldn't recommend broswerHistory.push() when you can have an onClick handler. Yes it works and will redirect, but is it the best thing to use? react-router also has a Link that should be used wherever possible. you get some nice additional features one for example would be an active route for whatever page you're on.
browserHistory.push() is a good way to handle redirecting when you do things like an auth login redirect or if you are responding to data from a request and conditionally taking them to a page.
<Route path={Paths.PROFILE} component={Profile} />
<Link to={pathToProfile(this.props.user.username, {myParams: 'here'})></Link>
what that would be translated into if you wanted to see the url from that exact link it would be /user/someUsername?myParams=here

Resources