In React 0.12.0 if you attempt to use this.key inside a react component, it is undefined
Simple test case here: http://jsfiddle.net/jg2pd4ob/2/
In 0.11.x you could access this value via this.props.key
which has been removed.
By design I believe, @sebmarkbage
Yea, this is by design. We had a note about it in the change log: http://facebook.github.io/react/blog/#changelog
The reason is that the concept of a key is something that is controlled by React internals before your component gets created. The same thing for refs.
You can think about an array of ReactElements
as a Map.
A Map is a series of key
and value
tuples. A React fragment is a series of key
and props
tuples (and also type
and ref
). The key is designed to decide what the value is in each slot, but not the actual value itself.
If you're reading a prop named key
you might be overloading the meaning of key
or accidentally using it for something unrelated.
This change makes the concept a bit more strict. This helps avoids bugs with transferring props to another component which shouldn't bring the key and ref along with it. It also helps performance by ensure that types in React internals are consistent and stable.
I would suggest renaming or duplicating the prop name as a possible fix if you really need to access it.
Thanks @sebmarkbage! I really appreciate the response. I kept reading the change log related to this, but I still couldn't figure out what it meant when it said key
and ref
were available on the element directly. The example of someElement.props.key -> someElement.key
seemed to pair with what I was expecting by changing this.props.key
to this.key
, but of course, that didn't work.
Anyway, I'll add a new prop, thanks again for the response.
100% agree with @dcneiner. The could be clarified better. The first line of the changelog reads:
key and ref moved off props object, now accessible on the element directly
The refs are accessible yes but not the key. Confusing.
@willdady this.refs
is the result of ref
assigned to children (not the value), just as the component updating or unmounting is a result of the key
assigned to "its associated element". Element refers to <div />
which is _not_ an instance. key
and ref
are defined as special use for React and their values should not be used for other purposes.
So to address what seems to be the confusion; <div key="foo" />.key === "foo"
is correct, this.key
/this.props.key
from within a component instance is not.
Thanks for the extra info @syranide – that makes sense as you explained it. Thanks!
+1for @willdady, I also tried this.key and had to google until I found an explanation/solution here: https://groups.google.com/d/msg/reactjs/MW2wIBi-pMg/ZrXwUdZUEa4J
Please clarify the blog post to prevent others from the headaches ;)
I would very much like to have access to the ref
attribute. Even if it is not directly accessable on the element, is there possibly some utility method to access that value?
I use that attribute as a way of accessing a model's key attribute. I recognize that you are suggesting the ref value should be semantically different than what I am proposing but I don't understand why this change is being forced. This is meaningful to me for use with https://github.com/jhudson8/react-backbone
Please, any way of accessing the ref going forward would be meaningful - even if there is a separate utility method that would return this value of a component is passed as a parameter. Thank you.
@jhudson8 I think for now this._currentElement.ref
should do the trick, but as far as I understand this will also be removed in the future.
@hpcodecraft That's already gone in master.
@jhudson8 It's likely that ref will change from a string to a richer data type which allow you to share a reference between siblings. See https://github.com/facebook/react/issues/1373 Could something like that work?
Well, I think ultimately I just need to forget about what I am wanting because I'm not going to stop this train. And, I do see the advantages of not using string references - it just made things much more convenient. Thanks for your time.
@jhudson8 We take breaking changes very seriously. We want to make sure that it's always at least possible to upgrade every use case... and ideally make it somewhat convenient. I think that either the first-class refs or context will be able to help you solve the use case though. :)
Feel free to ping me if you have trouble solving the upgrade path.
Key should be accessible via a getter. There are many instances when one would need to know which unique child in an array of children something happened to. A simple example would be a list of items that get parent.handler
passed from down from a controller view to its children as a prop. Say the handler takes a key
param and sets something on parent's internal state, then passes down an isSelected
prop to the child (in parent's render method) like:
dataset.map(function(record,index){
return (<child onSelect={parent.handle} key={index} isSelected={parent.isSelected === index}/>);
});
If key isn't accessible, now we need to pass another arbitrary prop, like keyIndex
with the same value to child. It's redundant.
@epferrari You could always bind parent.handle
to your index
I imagine?
@mathieumg yeah, but man does React complain when you bind a component method to the component! I suppose one could use a utility function to curry index
to parent.handle
, but either way is contrived. It's not that awful to add an extra prop, it's just counter-intuitive I suppose. They don't want key
reset in the component, and since you pass it by reference on props
, I suppose someone could overwrite it by accident. IF they don't know what they're doing that is. But of course that's solved with a getter
. Anyway, seems lots of things in React are built to keep novices from shooting themselves in the foot, which is both good and bad depending. Cest la vie.
React shouldn't complain if you provide a second argument. E.g. this.method.bind(this, index)
should work without warnings since that is not useless.
How about that... never tried with an argument, just scoffed at the warning and moved on. Thanks for the tip!
TLDR:
key={key}
ckey={'c'+key} // react wants you to build keys, but not use them
@caseys lol that's so true.
So, we can't access the keys? why not to provide a getter, is that hard?
I have an use case where accesing the keys are useful:
jsx-to-string: It's a npm module to transform from jsx to string. If you use this and a syntax highlight you can create a react component that displays the code of his children, useful for examples, but not that useful if you want to display code using keys. If you pass a component with keys, you can't see the key. GREAT! ok no, it's not.
So yeah, react wants you to add keys to your components, but at the same time, it deny the access to it. If keys are react internals, then they shouldn't be available to the user in the first place.
@erasmo-marin
Your conclusion is wrong. The key is not available from inside the component itself, but it is readily available from the element (which is the use case for jsx-to-string
). I commented on it here: https://github.com/grommet/jsx-to-string/issues/19#issuecomment-277810168.
By the way, we officially maintain and recommend to use pretty-format with ReactElement
plugin for printing JSX.
@erasmo-marin I'm curious what your use-case is . key
already has a purpose and it seems that you're trying to add a second meaning to it when used with your component which seems like a bad idea if it's for convenience (i.e. not having to add another property with a similar value).
@gaearon yeah, figured out that, thanks, for the explanation
@syranide check the comment by @gaearon, it clarifies the whole thing :)
Thinking in a future maintenance of my app, in cases that I need to write a custom key, how can I write an automated test that testing if exists and value of the key? is it important to test this scenario?
@syranide
Hello, I have use cases. Let me get this out of the way: I'm against the notion that the key prop should only be meant for React's under-the-hood rendering algorithm. The fact of the matter is, it's a unique key that I give it and can have many valuable uses if used responsibly.
usecase1)
I would like to be able to access a copy of the key name within the component. I could apply its name as the html div id for the rendering of the component. This is very useful for multiple instances of the same component rendered via a loop where their keys are given a generic key name with the loop number tacked on at the end. I would know that their rendered divs now also have unique ids for them with the same naming convention.
usecase 2
Also, let's say I have subcomponents within that component requiring keys. I could create the subcomponent keys by concatenating my choice for unique subcomponent key names onto the parent's key to create a subcomponent key that is a concatenation of both the parent's key along with the subcomponent's unique part. And this pattern continues. This assures unique and fully traceable keys from deepest subcomponents up to their greatest parents.
Conclusion
This is by no means the end of the world if it doesn't happen. But it means I'll basically be making my own prop that's just a duplicate of the key prop to accomplish this and cluttering up my component props in the process. I get the feeling that this whole notion of preventing key prop access within the component is based on the idea that people need to be protected from their own ignorance and misuse. I understand that concern, but it's too restricting. It would be nice to have a special function to access a copy of the key prop within the component. That way, since it can't be accessed like other props via props.key, people will still know that it's a special prop that does other stuff.
@learis Think of if it (and it literally is), the key in a Map. If you get the value for a given key and pass it to a function, then all that function will have access to the is value unless otherwise explicitly passed on as part of the API. The purpose of key here though is only to facilitate identity and may even contain "sensitive" information, and is not intended for the child to access. So while it may be convenient to access the key, it is not sanitary. It's much better to explicitly make it part of your component, even though it may seem redundant.
This is very useful for multiple instances of the same component rendered via a loop where their keys are given a generic key name with the loop number tacked on at the end. I would know that their rendered divs now also have unique ids for them with the same naming convention.
This wouldn't actually give you unique IDs because they would be only unique amongst siblings. So only one level up. So this would give you a false sense of assurance. We do have something in the works for this use case but it's not related to keys: #17322.
Also, let's say I have subcomponents within that component requiring keys. I could create the subcomponent keys by concatenating my choice for unique subcomponent key names onto the parent's key to create a subcomponent key that is a concatenation of both the parent's key along with the subcomponent's unique part. And this pattern continues. This assures unique and fully traceable keys from deepest subcomponents up to their greatest parents.
I'm not sure which use case this is solving. This sounds like a description of a solution, not a problem.
I get the feeling that this whole notion of preventing key prop access within the component is based on the idea that people need to be protected from their own ignorance and misuse.
It's not necessarily "misuse". It's that this doesn't scale to larger teams. We've tried that already — it used to be on the props. We moved it out.
The reason it doesn't work is the following:
databaseid-groupid
).Since most likely eventually you'll want to separate them anyway, we force you to do it early before it becomes a problem.
@gaearon
What you described is absolutely an example of ignorance and misuse among a team. Ultimately key is nothing more than a string value, and your example illustrates a team screwing up what that string value is used for. But it is what it is. If the higher-ups who design React had to deal with the issue occurring over and over again, then I guess it is better in this case to protect people from their own ignorance. If I have to pass another prop with the same string value as key just to gain access to that string value, so be it, it's not that big a deal :)
I think "ignorance" implies people lack the knowledge, and if they were taught properly, everything would be great. But what I'm talking about is locality. Generally, we want to be able to make a change to a component without having to make sweeping changes across its dependency graph. Putting key in props violates locality because it makes it no longer safe to change key
locally. It would have consequences scoped to the whole subtree. This is what we want to avoid. We want local problems to have local solutions.
@gaearon
Thank you for explaining it further. After looking at your example again along with your explanation on locality, this concern makes much more sense to me than previously. My sole interest in using key was my use cases 1 & 2 which I don't think I explained that clearly. They are the same use case, which is to have a nice naming system for the html element id's within components. My use case 2 just describes how that naming system can be assured to be unique among any component (parent or children) and not just siblings. I liked this naming system because it's descriptive in that it gives you a "bread-crumb" trail of the full ancestry of any component since it has all past component's id's tacked on to it.
But this is practically the only situation I can think of where key is used elsewhere for what it truly represents (which you mentioned earlier): an id that is unique among its sibling components. And as you have shown, this locality issue can spiral into a huge problem. Thanks again for the patience in explaining it to me.
My use case 2 just describes how that naming system can be assured to be unique among any component (parent or children) and not just siblings. I liked this naming system because it's descriptive in that it gives you a "bread-crumb" trail of the full ancestry of any component since it has all past component's id's tacked on to it.
Ironically we did have something like this as an implementation detail before React 15. Each DOM node had a data-reactid
which would be a full keypath, like you're describing. However, people generally disliked extra attributes in the DOM, so we got rid of it when the implementation didn't depend on it anymore.
Curious how would you use such an ID? Generally speaking, if you're debugging where an element comes from, React DevTools helps a lot.
Curious how would you use such an ID? Generally speaking, if you're debugging where an element comes from, React DevTools helps a lot.
I have just finished schooling and am making my first websites from the ground up. This was just an idea that popped into my head that seemed like it could be very useful. I have little experience going deep into debugging so it very well may be that this idea is unneeded even though it sounds really useful to me in concept. In the future, if it proves to be useful in a way that can't be accomplished through DevTools or other things, I'll bring it up. Take care and happy coding.
For future reference, if you are creating a set of JSX elements with array.map(item, index)
and want to pass key
as a prop e.g. <MyElement key={index} />
, just name the prop something else, e.g. <MyElement accessibleKey={index} />
and it won't be undefined when accessed from the component.
Most helpful comment
Yea, this is by design. We had a note about it in the change log: http://facebook.github.io/react/blog/#changelog
The reason is that the concept of a key is something that is controlled by React internals before your component gets created. The same thing for refs.
You can think about an array of
ReactElements
as a Map.A Map is a series of
key
andvalue
tuples. A React fragment is a series ofkey
andprops
tuples (and alsotype
andref
). The key is designed to decide what the value is in each slot, but not the actual value itself.If you're reading a prop named
key
you might be overloading the meaning ofkey
or accidentally using it for something unrelated.This change makes the concept a bit more strict. This helps avoids bugs with transferring props to another component which shouldn't bring the key and ref along with it. It also helps performance by ensure that types in React internals are consistent and stable.
I would suggest renaming or duplicating the prop name as a possible fix if you really need to access it.