diff --git a/src/isomorphic/classic/types/ReactPropTypes.js b/src/isomorphic/classic/types/ReactPropTypes.js index 5382931bb47b0..250531f2e7873 100644 --- a/src/isomorphic/classic/types/ReactPropTypes.js +++ b/src/isomorphic/classic/types/ReactPropTypes.js @@ -16,6 +16,7 @@ var ReactFragment = require('ReactFragment'); var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames'); var emptyFunction = require('emptyFunction'); +var getIteratorFn = require('getIteratorFn'); /** * Collection of methods that allow declaration and validation of props that are @@ -345,12 +346,37 @@ function isNode(propValue) { if (propValue === null || ReactElement.isValidElement(propValue)) { return true; } - propValue = ReactFragment.extractIfFragment(propValue); - for (var k in propValue) { - if (!isNode(propValue[k])) { - return false; + + var iteratorFn = getIteratorFn(propValue); + if (iteratorFn) { + var iterator = iteratorFn.call(propValue); + var step; + if (iteratorFn !== propValue.entries) { + while (!(step = iterator.next()).done) { + if (!isNode(step.value)) { + return false; + } + } + } else { + // Iterator will provide entry [k,v] tuples rather than values. + while (!(step = iterator.next()).done) { + var entry = step.value; + if (entry) { + if (!isNode(entry[1])) { + return false; + } + } + } + } + } else { + propValue = ReactFragment.extractIfFragment(propValue); + for (var k in propValue) { + if (!isNode(propValue[k])) { + return false; + } } } + return true; default: return false; diff --git a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js index 7c37fa59607f4..412d0f2e695c9 100644 --- a/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js +++ b/src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js @@ -405,6 +405,39 @@ describe('ReactPropTypes', function() { }); }); + it('should not warn for iterables', function() { + var iterable = { + '@@iterator': function() { + var i = 0; + return { + next: function() { + var done = ++i > 2; + return {value: done ? undefined : , done: done}; + }, + }; + }, + }; + + typeCheckPass(PropTypes.node, iterable); + }); + + it('should not warn for entry iterables', function() { + var iterable = { + '@@iterator': function() { + var i = 0; + return { + next: function() { + var done = ++i > 2; + return {value: done ? undefined : ['#' + i, ], done: done}; + }, + }; + }, + }; + iterable.entries = iterable['@@iterator']; + + typeCheckPass(PropTypes.node, iterable); + }); + it('should not warn for null/undefined if not required', function() { typeCheckPass(PropTypes.node, null); typeCheckPass(PropTypes.node, undefined);