-
Notifications
You must be signed in to change notification settings - Fork 46.8k
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
Iterators are not supported inside Component.render #11502
Comments
Did it work in 15? |
No |
Maybe these fiddles may only work in modern browsers: Please compare the results of React 15.6.2 (working fine): with the results of React 16 (not working properly): |
Can you look into why this is not working? The relevant code is in |
Okay I checked that in detail: First: It's not a "React15 vs React16" thing but more like a "react.development" vs "react.production" thing. If you use the React production modules, the example also works with React16: Reason: For details see: react/packages/react/src/React.js Line 47 in 365c2db
This "createElementWithValidation" function uses a local function called "validateChildKeys" which iterates each item of an array/iterable/iterator child to check whether the "key" attributes are set properly. For details see:
As an iterator can only be fully iterated once (in contrast to an "iterator aggregate" aka. "iterable") the items of an external iterator will not be rendered in the browser (because the second time when ReactDOM tries to iterate the iterator it will not get any results). For those who are interested in more details check: In my personal opinion, it's ALWAYS a very bad idea to pass iterators around directly (though I know this is unfortunatelly and unnecessarily done quite often). Iterators always have an internal state that will be modified on each iteration step. This more or less hidden side effect will sooner or later be a source of trouble (like in this issue here). Means: Do NOT use: const elements = function * () {
yield 1;
yield 2;
yield 3;
} Instead ALWAYS use something like const elements = {
[Symbol.iterator]: function *() {
yield 1;
yield 2;
yield 3;
}
} I personally prefer to use some "Seq" class that is specialized for lazy sequencing (with "filter", "map", "reduce" etc.), which allows things like: const elements = Seq.from(function *() {
yield 1;
yield 2;
yield 3;
}); |
Oh. I get it now. This is pretty subtle 😞 |
Thanks, guys. I was using Should results of this discussion be part of an official React documentation? Or I should fill another issue for this? It would be helpful to see the difference between DEV and PRODUCTION modes, see if iterators are supported and how to work-around for DEV. |
Is it possible for us to warn if you pass the wrong thing, somehow? I agree it's confusing. On the other hand, it's good that it ends up being broken in development mode because at least in this case you notice. It would've been worse if it worked in development but broke in production. We could document it but I don't see a good place. If you have ideas please raise it on the docs repository. |
@mcjazzyfunky Could you please provide the source code of this |
Example of this Seq implementation:
Result:
|
@askoretskiy: I really like your Seq implementation.
const seq = createSeq(function * () {....});
// [Edit: removed stupid proposal]
https://github.com/js-works/js-essential/blob/785eaf6b6fd6fbdd7ef863396306924733fc0a7e/src/main/Seq.js Also please be aware that my implementation is not optimized for Seqs based on arrays (AFAIK, Facebooks Seq classes are optimized for that): // create an array with 1000 undedfined elements
const array = new Array(1000);
// the following works, but does not work in constant time,
// as there's no optimization implemented (yet) for the array case.
const size = Seq.from(array).count(); Also please do not copy that ".bind(this)" stuff, your "that = this" solution is surely much better than using bind. Last but not least: |
@mcjazzyfunky Thanks, I may extend my implementation with some new tricks )) Yes, one could write really lean code if he opts in to use ES2015. |
Pasting this here, which I incorrectly pasted as a solution to #11864 (which should have a similar fix on the the render result): React can fix this without too much juggling, essentially have // EDIT: Don't treat component types that are for some reason iterable like arrays!
// EDIT: What about iterable items of props.children?
const fixedArgs = Array.prototype.slice.call(arguments, 1).map(arg => {
const iteratorFn = arg[ITERATOR_SYMBOL]; // actually check all the polyfills
if (!iteratorFn) return arg;
return [...iteratorFn()]; // iterators (as opposed to iterables) can only give their results once.
});
const element = createElement.apply(null, fixedArgs);
...
validateChildKeys(fixedArgs[i], type);
... |
We're going to start warning about this. |
What is the current behavior?
When you provide a list of React components -- its works. When you provide an iterator of React components -- it does not work.
This works:
Doesn't work:
Unfortunately, I couldn't make it run on JSFiddle, maybe it has no support of iterators.
What is the expected behavior?
It should render exactly the same version as list-based one:
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
react-dom 16.0.0 and 15.x, browser-independent (I tried in Firefox 56).
The text was updated successfully, but these errors were encountered: