In my react app with react-router v6 I am retrieving links dynamically that can be either a regular external url (like https://stackoverflow.com/) or an internal path (like "/my-page") that corresponds to a Route with react-router.
So far, when using react-router-dom's <Link> component, I could only get it to work with an internal path, but NOT with an external url. As far as I understood the docs I couldn't find a way to achieve this, at least not with v6.
So the best approach I've come up with so far is to write an own MyLink component that either render an <a> or a <Link> depending on whether the href is external or not:
function MyLink(props) {
const isHrefExternal = props.href.match(/^http|^https|^www/);
if (isHrefExternal) {
return <a href={props.href}>{props.children}</a>;
}
return <Link to={props.href}>{props.children}</Link>;
}
This of course doesn't look like a very good solution:
The check for isHrefExternal is very naive
It's unclear what props MyLink should accept and how they should be managed since <a> and <Link> have different props.
For a full example see this codesandbox
Can you tell me how to do it better? Ideally there would be an option to pass an external url to react-router-dom's <Link> component but I couldn't find one.
Thanks a lot!
The Link component handles only internal links within your app, and likely won't ever handle external links natively.
I don't see any overt issues with your code, and I don't think it's a bad solution. The "isExternal" check may be naive, but it only needs to cover your specific use cases. As new use cases are required you can tweak the logic for this check. In my projects I've worked with we've just typically included an isExternal type property with fetched data so the app doesn't even need to think about what is or isn't an external link, it just checks the flag when rendering.
The react documentation says that the match object contains a param property assigned to an object with Key/value pairs parsed from the URL corresponding to the dynamic segments of the path.
When I try to pass key value pairs as parameters, I only get the key, but the value is always undefined.
<Route path="/location:color?" component={Location} />
If I do this, and then in the Location component, I console log props.match, I get the match object, but when I open it, I get this: params: {color: undefined}.
How do I pass value too?
Also, I feel like I'm misunderstanding something about these path parameters, because what exactly is the point of them? If I wanted to pass any value to any component, I can just use props. What is the purpose of these path parameters?
For all complex data structures, I recommand parsing to string and deserialize at the other end. it may be a good practice if everyone in your team is doing it this way.
Example :
KeyValuePair : <Color,Blue>
Parse it to string : "\"Color\",\"Blue\"
The idea of using path="/location/:color?":
is to fetch/navigate to a page according to its id to get information
For example, I want to get student information on the page, first, it goes to URL www.location/studentId it will take the student id to know which student to view on the page.
In your case, you get undefined because you didn't pass any color value, so you need to pass the value to navigate as follow:
- change `<Route path="/location:color?" component={Location} />`
to `<Route path="/location/:color?" component={Location} />`
- To navigate use `this.props.history.replace("/location/" + this.props.color);`
Please read this article it will give you more ideas about react-routesreact routes Article
or React routes documentation
I'm building a deeply nested component that can be navigated to a lot of different ways, so I'm using a wildcard so I don't have to write out all the possible routes, something like:
<Route path={'**/post/new'}>
<CreatePost {...props} />
</Route>
However, now I need to get and use information from where the user came from in the create post component, so if their paths were:
/en/10249/discussion/13353/thread/24/post/new
/en/10015/discussion/14233/post/new
/fr/10350/link/155/post/new
I would need the params:
/en/:b_Id/discussion/:d_Id/thread/:t_Id/post/new
/en/:b_Id/discussion/:d_Id/post/new
/fr/:b_Id/link/:l_Id/post/new
including en, fr, discussion, thread and link, but because I replaced the params with a wildcard I don't have access to those params anymore. These are just a small subset of path examples that can reach /post/new, and why I needed to replace all routes with a wildcard.
I'm currently thinking of parsing the URL by splitting it by / and reading relevant data that way, but I'm looking if there are better alternative approaches.
Is there a way I can write this where I don't need to type out every possible route, while still splitting up the path into separate params?
react-router automatically includes path parameters in the component props.
In your <CreatePost> component, use:
this.props.match.params.d_Id
and so on.
For more info, see this question.
I have a path in the format of /somePath/:name where the :name needs to be one of the known strings. I read that the Route component supports an array of URLs to match, so it's easy to generate a list of supported paths, but doing so takes away the convenient of automatically capturing the part of the path.
For example, I currently have a Route component defined like this below.
<Route exact path={SomeKnownNames.map(n => `/somePath/${n}`)} component={SomeRoute} />} />
This should work, but I cannot access the value in props.match.params in the Route component anymore. Is there a way to achieve this without manually parsing the URL?
Let me add that I do NOT want to match if the value of :name is not in the known strings.
I'm using react-router-dom v5.
Apparently, this works:
<Route exact path={SomeKnownNames.map(n => `/somePath/:name(${n})`)} component={SomeRoute} />} />
i.e. If you do /somePath/:name(foo), then it matches /somePath/foo, and foo is captured in props.match.params.name in the route component.
Have you tried doing this:
path={SomeKnownNames.map(n => `/somePath/:${n}`)}
Adding the : allows the names to be treated as params. Try it out, let me know if that is unrelated. I may need more information to better answer your question.
Also, try to use the useParams() hook from React Router. What does it show in your component?
Update: path-to-regexp was removed from React V6, so this answer is obsolete.
React Router uses the path-to-regexp library, which allows you to provide your own regex for a parameter. See https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#custom-match-parameters.
For example, if you want to match /somePath/foo, /somePath/bar, and /somePath/baz, and have match.params contain myParam: "foo", myParam: "bar", or myParam: "baz", use this pattern:
<Route
exact
path="/somePath/:myParam(foo|bar|baz)"
component={SomeRoute}
/>
I saw that React 16 allows for attributes to be passed through to the DOM. So, that means 'class' can be used instead of className, right?
I'm just wondering if there are advantages to still using className over class, besides being backwards compatible with previous versions of React.
class is a keyword in javascript and JSX is an extension of javascript. That's the principal reason why React uses className instead of class.
Nothing has changed in that regard.
To expand this a bit more. A keyword means that a token has a special meaning in a language syntax. For example in:
class MyClass extends React.Class {
Token class denotes that the next token is an identifier and what follows is a class declaration. See Javascript Keywords + Reserved Words.
The fact that a token is a keyword means that we cannot use it in some expressions, e.g.
// invalid in older versions on Javascript, valid in modern javascript
const props = {
class: 'css class'
}
// valid in all versions of Javascript
const props = {
'class': 'css class'
};
// invalid!
var class = 'css';
// valid
var clazz = 'css';
// valid
props.class = 'css';
// valid
props['class'] = 'css';
One of the problems is that nobody can know whether some other problem won't arise in the future. Every programming language is still evolving and class can be actually used in some new conflicting syntax.
No such problems exist with className.
Update (August 2020):
A comment by Dan Abramov on the same thread:
This was the most controversial part of the proposal. Since then, we
released Hooks, which encourage writing function components. In
function components, we generally suggest using destructuring for
props, but you can't write { class, ... } because it would be a syntax
error. So overall it's not clear that this is ergonomic enough to
actually follow through with. I think it's plausible we'll revisit
this in the future, or at least make class not warn and let people do
what they want. But for now, we'll shelving this idea.
so, nope
(August 2018)
The React team is actually going to switch to class instead of className in the upcoming future (source):
className → class (#4331, see also #13525 (comment) below). This has
been proposed countless times. We're already allowing passing class
down to the DOM node in React 16. The confusion this is creating is
not worth the syntax limitations it's trying to protect against.
Why switch and not support both?
If we support both without warnings, then the community will split
over which one to use. Each component on npm that accepts a class prop
will have to remember to forward both. If even one component in the
middle doesn't play along and implements only one prop, the class gets
lost — or you risk ending up with class and className at the bottom
"disagreeing" with each other, with no way for React to resolve that
conflict. So we think that would be worse than status quo, and want to
avoid this.
So you should stay tuned.
I would still recommend using className as long as this is what the API expects.
Just to shed a little more light, on top of the other good answers already given:
You'll notice that React uses className instead of the
traditional DOM class. From the docs, "Since JSX is JavaScript,
identifiers such as class and for are discouraged as XML attribute
names. Instead, React DOM components expect DOM property names like
className and htmlFor, respectively."
http://buildwithreact.com/tutorial/jsx
Also, to quote zpao (a React contributor / facebook employee)
Our DOM components use (mostly) the JS API so we opted to use the JS
properties (node.className, not node.class).
as of june 2019, the process of changing className to class has been halted, it could be continued later
here is the post by facebook dev explain why
https://github.com/facebook/react/issues/13525
React docs recommend on using cannonical React attribute names rather than the conventional Javascript naming, so even when React allows attributes to be passed through to DOM, it will give you a warning.
From the docs:
Known attributes with a different canonical React name:
<div tabindex="-1" />
<div class="hi" />
React 15: Warns and ignores them.
React 16: Warns but converts values to strings and passes them through.
Note: always use the canonical React naming for all supported attributes.
Class versus className in reactJS
Class is a reserved word or keyword in reactJS as much as function is in the javascript. That is why we use the word "className" to refer to the class.
There is no real explanation by React team on this but one would presume it to be differentiated from reserved keyword "class" in Javascript since its introduction in ES2015+.
Even if you use "class" in element configuration while creating element, it won't throw any compilation/rendering error.
In ReactJS, we are dealing with JSX and not HTML as you all know. The JSX wants you to use className because it is an underlying javascript DOM API! class being a reserved keyword in JS is not the primary reason why we are not using class and instead, using className. It is because we are referring to that DOM API
Firstly, let's think why className was used over class in the first place:
I recently watched a CSS in React conference video by Joel Denning who disagrees that className was used because class is a keyword in JavaScript. I am leaning towards agreeing with him, although I'm still not fully clear. What follows is the explanation from his video and some of my input:
Open up babel and compile the following JSX snippet:
const element = <div className="foo" />;
const element2 = <div class="foo" />;
Babel compiles this to:
var element = /*#__PURE__*/React.createElement("div", {
className: "foo"
});
var element2 = /*#__PURE__*/React.createElement("div", {
"class": "foo"
});
See how class is treated as a string "class", so no issues with JavaScript keywords.
HTML elements have attributes like class, and then once parsed a DOM node is created with properties like className. Difference between attributes and properties? Take a look at What is the difference between properties and attributes in HTML?.
HTML attribute values can only be strings, whereas DOM properties can have any value. When it comes to dealing with class names I'm not sure how this is useful, but it's certainly cleaner to update DOM properties than attributes:
const div = document.createElement('div');
// Attribute update
div.setAttribute('class', 'foo');
// Property update
div.className = 'foo';
Also, updating a DOM property triggers a re-render which ties in nicely with the idea that the name of a class is a mutable state.
Attributes tend to be used to initialise DOM properties, however, the class attribute and className property are reflected i.e. updating the className property causes the class attribute to be updated with the same value... This makes the reasoning as to why className was chosen confusing, maybe it's because semantically properties are associated with values that can update? I have no idea...
It's so confusing that React are allowing class usage alongisde className (as mentioned in the question). From https://github.com/facebook/react/issues/13525:
className → class (#4331, see also #13525 (comment) below). This has been proposed countless times. We're already allowing passing class down to the DOM node in React 16. The confusion this is creating is not worth the syntax limitations it's trying to protect against. We wouldn't do this change by itself, but combined with everything else above it makes sense. Note we can’t just allow both without warnings because this makes it very difficult for a component ecosystem to handle. Each component would need to learn to handle both correctly, and there is a risk of them conflicting. Since many components process className (for example by appending to it), it’s too error-prone.
In my opinion, there is no concrete answer to this question. I like the semantics behind using a property over an attribute to update something, but if you took me back in time to when the decision was made to use className over class I would have said it is unnecessary.
tldr; To answer your question, I would stick with className which avoids the warnings of class, and is the approach that most other React developers are familiar with so your code will be easier to read.
Still not satisfied? Take a read of https://github.com/facebook/react/issues/13525#issuecomment-417818906.
The problem is that the react code you see is not HTML - it is in fact JSX is an extension to javascript (basically javascript + plus a little bit more).
Q:Can we use class to represent the HTML attribute: 'class'?
Given that it is javascript, you cannot use the word class because that is a special javascript word that is "reserved" (javascript prescribes certain words that you can and cannot use).
Given that class is a reserved word, how are you going to write classes in our "html" code jsx, because that word is a 'reserved' word and is not allowed to be used like that? The way around it is to use: "className" instead of "class", so then jsx will know that you are dealing with the html class attribute and not the javascript class keyword.
class can't be used instead of className in React 16, as well as in the former versions.
The reason for this is a bit obscured, probably it's some kind of convention or so.
If this explanation isn't good enough for you, check out my article on hashnode. It's a long and in-depth write-up, therefore your curiosity will probably be satisfied.
you have to use className instead of class in your div code section area