facebook/react-native

undefined is not a function(evaluating '_iterator[typeof Symbol === "function"?Symbol.iterator:"@@iterator"]()')

hzuhyb opened this issue · 29 comments

I used "for(let...of...){}" to traverse a Map object at react-native,I got this error.
it's working perfectly for IOS and android debug mode, but not working for android normal mode.

wx20170912-162205 2x

When using remote debugging you use chrome for running JS code. It's a bit different.

@radko93 thank you for your reply, I know this difference, but how I fix this, whether I need to do some compatibility handling or whether i can not use "for(let...of...){}" to traverse a Map object at react-native?

I had the same problem a few days back and I think I solved it with an babel preset. I used
["react-native-stage-0", "react-native-stage-0/decorator-support"]
babel-preset-react-native-stage-0

Hey, thanks for reporting this issue! It looks like your description is missing some necessary information, or the list of reproduction steps is not complete. Can you please add all the details specified in the Issue Template? This is necessary for people to be able to understand and reproduce the issue being reported. I am going to close this, but feel free to open a new issue with the additional information provided. Thanks! See "What to Expect from Maintainers" to learn more.

@huangtubiao Have you solved this yet?

@luoyushouchai i solved it by using other traversal methods, you also solve it with an babel preset.

@huangtubiao @TheNoim
How should I do to change this?
I tried : npm install babel-preset-react-native-stage-0 --save
and then modify .babelrc in root project : { "presets": ["react-native-stage-0"] }
It still not work!

Ok.. so React Native internal code is now using for..of iterators -- and they don't work for me!

Edit: the following does work, though it doesn't explain why I have to apply this fix myself.

import 'core-js/es6/map'
import 'core-js/es6/symbol'
import 'core-js/fn/symbol/iterator'

Edit 2: Just kidding, that doesn't work either.

@lwansbrough when you say that doesn't work either, are you getting a runtime error to the effect of Incompatible receiver, Map required!? If so, check out a possible solution here zloirock/core-js#368

@ericketts as you said, I got this mistake at different computer machine and I haven't found a good solution yet.
Incompatible receiver, Map required!
wechatimg4066

Sorry that was meant to contain a link directly to the comment which specifies a (temporary) fix. Fixed the link now.

the main issue here is that Symbol is not implemented in Android.

you have to manually polyfill Symbol and methods that accepts Symbol.iterator.

In my case, a third party library was causing a crash since it uses for-of loop.

Here's my solution:

if (Platform.OS === 'android') {
  if (typeof Symbol === 'undefined') {
    logger.info('Polyfilling Symbol on Android');
    require('es6-symbol/implement');
    logger.info('Polyfilling Array.prototype[Symbol.iterator] on Android');
    if (Array.prototype[Symbol.iterator] === undefined) {
      Array.prototype[Symbol.iterator] = function() {
        let i = 0;
        return {
          next: () => ({
            done: i >= this.length,
            value: this[i++],
          }),
        };
      };
    }
  }
}

In my case. I had to polyfill Array[Symbol.iterator] to return a Iterator protocol:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols

@vafada I tried your approach but now i'm getting:
image

Any ideas? Did you have to add any additional packages?

@Seddy24 I've seen that error when I imported

import 'core-js/es6/symbol'
import 'core-js/fn/symbol/iterator'

Make sure you dont import those libraries.

The fix above requires you to add es6-symbol in your package.json

The simplest way to fix the

undefined is not a function(evaluating '_iterator[typeof Symbol === "function"?Symbol.iterator:"@@iterator"]()')

error without any libraries is to do:

if (Platform.OS === 'android') {
  if (typeof Symbol === 'undefined') {
    if (Array.prototype['@@iterator'] === undefined) {
      Array.prototype['@@iterator'] = function() {
        let i = 0;
        return {
          next: () => ({
            done: i >= this.length,
            value: this[i++],
          }),
        };
      };
    }
  }
}

Add the poly fill in your index.js or whatever is your App's entry point

@vafada Thanks that worked. I had to create a brand new app as I couldn't get it to work with what I had but I'm working on a POC anyway so thanks!!

@Seddy24 no reason to reinvent the wheel, corejs is great (and already included as a dependency of react-native). try this:

// symbol polyfills
global.Symbol = require('core-js/es6/symbol');
require('core-js/fn/symbol/iterator');

// collection fn polyfills
require('core-js/fn/map');
require('core-js/fn/set');
require('core-js/fn/array/find');

for whatever reason react-native on android is shipping an old as shit version of JSC, one that doesn't have support for language features that current react version needs to work 🙄

@ericketts Thanks for the additional information. I'm new to react / react native, trying to rewrite my Xamarin app and loving react native so far!! Any additional tips are welcome!!

I dont know but probably doing something wrong, copy/paste from @ericketts is not working in my app :( had to rewrite to for of to for

@ivanpagac try pinning corejs to version 2.5.2 (this can be easily accomplished using yarn, if you're using npm still I'd say switch to yarn)

as a more permanent solution to this issue, I'd advise updating the JSC version that ships with android so that the functionality presently being pollyfilled is implemented natively.

@ericketts Thanks for that! The solution with updating the JSC version works.

@ivanpagac glad to hear it! Really extremely puzzling to me why RN continues to ship a super outdated JSC.

@ericketts into which file exactly does the code you mentioned above go?

`// symbol polyfills
global.Symbol = require('core-js/es6/symbol');
require('core-js/fn/symbol/iterator');

// collection fn polyfills
require('core-js/fn/map');
require('core-js/fn/set');
require('core-js/fn/array/find');`

Thank you`

@Calsowah I created a file polyfill.js, and require it in the index.js or app.js file before requireing any other dependencies (note I said requireing, not importing, as the semantics of require are easier to reason about, and to guarantee ordering of imports).

Edit: also for blocks of code, you need 3 backticks on their own line to start and end the block, like so:

```javascript
const x = 4;
```

which becomes

const x = 4;

@ericketts I did as you suggested but I'm getting the following error
screenshot_2018-06-28-12-56-46

If polyfilling Array.prototype['@@iterator'] does not fix the issue, you might need to polyfill String.prototype['@@iterator'] as well.

@ericketts Thanks for the upgrading JSC tip. Works flawlessly!

The JSC in Android was updated in December, and it will be part of the 0.59 release.