-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Warn if props obj passed into createElement / cloneElement inherits from anything other than Object #6134
Conversation
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla - and if you have received this in error or have any questions, please drop us a line at cla@fb.com. Thanks! |
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks! |
It's failing linting because of the |
@spicyj Do we still do some cheating with the props object in a React-internal component? I remember us doing something in the past… |
@zpao TopLevelWrapper in ReactMount does but it doesn't use createElement. |
I think it’s fine to put this between |
@@ -127,6 +127,10 @@ ReactElement.createElement = function(type, config, children) { | |||
|
|||
if (config != null) { | |||
if (__DEV__) { | |||
warning( | |||
!(config.__proto__ != null && config.__proto__ !== Object.prototype), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let’s avoid double negations. You want to specify the “normal” condition in the warning
call. The normal condition is when the __proto__
ain’t there or it is Object.prototype
. So let’s write it like
warning(
config.__proto__ == null || config.__proto__ === Object.prototype,
instead.
This looks mostly good but let’s fix the linting and the couple of nits I left. Thanks for contributing! |
…rom anything other than Object
@richardscarrott updated the pull request. |
@gaearon I've made those changes, let me know if there's anything else that needs updating. |
Warn if props obj passed into createElement / cloneElement inherits from anything other than Object (cherry picked from commit 7b47e3e)
It's my understanding that the intent of warnings in React are "things you really should fix," but my app uses a Redux store full of custom Model objects that are really just POJOs with a couple static properties (so I can infer what the type of the backing data-model is). Now my console is full of warnings, but I don't think that there's anything actionable. I don't try to get data from some object up the prototype chain. Is there anything I can do? |
@sethkinast Can you give more details? React.createElement already does ignore the prototype chain so it is probably an error to use an object with a prototype. Moreover, React.createElement pulls off the "key" and "ref" properties so you'd also get strange behavior if those were to be present in the models you're using as props – and it's not clear to me what you would write if you did want to set a key or ref. We generally suggest putting your model one level lower if you want to pass it unchanged, so props would be like |
I have a Model class that is basically
Then, when I get data from various APIs and persist it to my store, I first use the data to create objects that extend from Model to process the data (coercing types, validating fields):
I use those models, which are really just plain objects that happen to have something along their proto chain, and render components that often correspond 1:1 to the model.
I could refactor all my components to instead do |
I would recommend putting toJS() {
return Object.assign({}, this);
} Then you can render() {
return <SomePersonComponent {...spicyj.toJS()} />;
} which doesn’t seem like a lot of hassle. |
The slightly confusing part about this is that any methods on it wouldn’t get bound, but seems like this is already an issue with the way you’re doing it. Maybe |
Yeah, these objects have no non-static methods, so that's not a concern. On Thu, May 26, 2016, 12:37 PM Dan Abramov notifications@github.com wrote:
|
I guess |
This wouldn’t let us warn at all though. |
It would for people calling React.createElement directly. I guess I think of |
That's actually exactly how I thought of |
@spicyj @gaearon I realize this may sound absurd, but I have a use-case for I'm writing a library for integrating redux and falcor. Lists are represented as maps in Falcor, which enables us to retrieve partial lists, or to have non-index keys in lists, e.g. We can't create Arrays for the output JSON because non-indexer keys don't survive serialization, but the downside is we give up the Array prototype methods for lists. But I've recently discovered that if I I've tested this works in practice, so now my only problem is React complaining that my props aren't strictly Objects. I don't expect |
I don't think I fully understand how you are using Falcor and how that relates to props, but basically: React.createElement looks at .key and .ref on the "props" second argument and the actual props that the component gets are copied off of that – it's not the same object. So passing another object with a non-Object prototype isn't actually useful because the component won't receive the prototype, and it obscures the fact that .key and .ref are treated specially. I think it's a good thing that React warns you in this case. If you want to pass an object with prototype methods, just pass it as any key on props – like |
@spicyj I don't think I've made myself clear. I don't mind that the Array prototype methods won't make it onto const rootProps = Object.create(Array.prototype);
React.render(
<RootComponent {...rootProps}/> // <-- complains that rootProps isn't an Object
); If you're not familiar, Falcor builds a JSON tree that you can pass to React. The JSON tree will contain maps and maps-that-represent-lists. Since Falcor can't distinguish between maps and Arrays, every branch in the tree will be created via falcorModel.get(
'genres[0..1].genreName',
'genres[0..1][0..1].movieName'
)
.subscribe(({ json }) => {
/*
json === {
genres: {
0: {
genreName: 'Action Movies',
0: { movieName: 'Die Hard' },
1: { movieName: 'Lethal Weapon' }
},
1: {
genreName: 'Romantic Comedies',
0: { movieName: 'The Notebook' },
1: { movieName: 'How to Lose A Guy in Ten Days' }
}
}
}
*/
React.render(<RootComponent {...json}/>); // <-- complains :(
}); |
This is the same sort of thing as the issue I mentioned earlier in this @spicyj I looked at the babel plugin that handles this transpilation but it On Mon, Aug 8, 2016, 6:17 PM Paul Taylor notifications@github.com wrote:
|
Agreed. I initially assumed the JSX Object-spread syntax behaved the same as ES6 Object-spread syntax, which doesn't complain about merging in this case 😁 |
This warning will be raised (incorrectly?) if const evaluate = require('eval');
const O2 = evaluate("module.exports = Object;", "example.js", {}, true);
console.log(O2 === Object); // output false
console.log(O2.prototype === Object.prototype); // output false Also see the issue reported on |
#3435