Search code examples
iosreactjsreact-nativereact-native-iosreact-native-web

"Unhandled JS Exception: Can't find variable setTimeout" in iOS only


I'm trying to create a react-native-for-web app to build for iOS and web platforms. A solution to my question will get the xcode/mac Simulator running with hot reloading of the iOS version, while also running the web version, of a "react-native-web": "^0.9.x" app.

I googled how to start one of these, and found the top few articles were written by the creator of create-react-native-web-app, so I decided to try this method. It has however been an uphill battle.

But first of all, the part that seems to work out of the box is the web part. In my first attempt, after running npx create-react-native-web-app demo-app, yarn web immediately worked. :)

But yarn iOS wouldn't build, and there were multiple issues.

I have node -v >> v11.5.0. I'm on Mohave, with xcode 10.1 already setup (for iOS development).

  • I needed to install the xcode 10.1 commandline tools though.
  • Then, I needed to yarn iOS
  • followed by opening the creaternwapp project under ios/ and change the Project Settings > Build System to Legacy Build System.
  • Then I had to attempt to build it in xcode. (turns out this is important, even though build will fail)
  • Then, (cd node_modules/react-native/third-party/glog-0.3.4/ && ./configure) // those numbers may change apparently, depending on install
  • whether I needed to or not, I changed the .babelrc from:

    {
        "presets": ["module:metro-react-native-babel-preset"],
    }
    

to:

{
  "presets": [["module:metro-react-native-babel-preset"], ["react-app"]],
  "ignore": ["node_modules/art/core/color.js"],
  "plugins": [
    ["module-resolver", {
      "alias": {
        "^react-native$": "react-native-web"
      }
    }]
  ],
}
  • then: npm install && npm audit fix and followed that with yarn install so yarn could get control back.

At this point yarn ios succeeds, but the error with setTimeout is showing on the simulator. I researched that, and apparently these sorts of errors happen when react-native install isn't complete, with the suggested solution to yarn upgrade react native. But yarn upgrade [email protected] doesn't change anything for me.


Solution

  • This is not the answer I was looking for, I would love create-react-native-web-app to work out of the box .. but for now -- here's how I am using rn + rnw , even with react-native-paper:

    I can describe how to get react-native-paper working in expo.

    1. expo init --yarn --template blank demo-app

    -- cd demo-app

    1. yarn add react-native-web react-router-dom react-router-native react-app-polyfill react-art react-native-paper react-dom

    -- yarn add -D react-scripts @babel/preset-flow @babel/plugin-proposal-class-properties

    1. code package.json and add scripts:

      "web": "react-scripts start", "build-web": "react-scripts build"

    -- we're going to be cheating and editing them in-place. A better practice is to copy node-modules/react-scripts/ config and scripts into your project folder, install their dependencies and get them working locally. But this is just a proof-of-concept (so .. just be sure not to reinstall node_modules or react-scripts for now on)

    -- configure/add main:

    "main": "react-native-main.js",

    1. code react-native-main.js saving:
    import { KeepAwake, registerRootComponent } from 'expo'
    import App from './src/App'
    
    if (__DEV__) {
      KeepAwake.activate()
    }
    
    registerRootComponent(App)
    
    1. mkdir src public

    2. rm App.js

    -- code src/App.js saving:

    import React from 'react'
    import { StyleSheet, View } from 'react-native'
    import { Provider as PaperProvider } from 'react-native-paper'
    import { Router, Switch, Route } from './routing'
    
    import Home from './Controllers/Home'
    
    export default class App extends React.Component {
      render () {
        return (
          <PaperProvider>
            <View style={styles.app}>
              <Router>
                <Route exact path="/" render={props => <Home {...props} />} />
              </Router>
            </View>
          </PaperProvider>
        )
      }
    }
    
    const styles = StyleSheet.create({
      app: {
        flex:1
      }
    })
    
    1. mkdir src/Controllers && code src/Controllers/Home.js saving: (need to make something to demo Paper .. this is essentially just the text example from the examples folder)
    /* @flow */
    
    import React from 'react'
    import { View, StyleSheet, Platform } from 'react-native'
    import {
      Caption,
      Headline,
      Paragraph,
      Subheading,
      Title,
      withTheme,
      type Theme,
    } from 'react-native-paper'
    
    type Props = {
      theme: Theme,
    };
    
    class TextExample extends React.Component<Props> {
      render() {
        const {
          theme: {
            colors: { background },
          },
        } = this.props
        return (
          <View style={[styles.container, { backgroundColor: background }]}>
            <Caption style={styles.text}>Home</Caption>
            <Paragraph style={styles.text}>This is the home component</Paragraph>
            <Subheading style={styles.text}>home component</Subheading>
            <Title style={styles.text}>Home</Title>
            <Headline style={styles.text}>Home on { Platform.OS }</Headline>
          </View>
        )
      }
    }
    const styles = StyleSheet.create({
      container: {
        padding: 16,
        flex: 1,
      },
      text: {
        marginVertical: 4,
      },
    })
    
    export default withTheme(TextExample)
    
    1. code public/index.html saving:
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Third Party Demo</title>
    </head>
    <body>
        <div id="react-native-web-app"></div>
    </body>
    </html>
    
    1. code src/index.js saving:
    import React from 'react'
    import ReactDom from 'react-dom'
    
    import App from './App'
    
    ReactDom.render(<App />, document.getElementById('react-native-web-app'))
    
    1. code src/routing.native.js saving:

    export { NativeRouter as Router, Switch, Route, Link } from 'react-router-native'

    -- code src/routing.web.js saving:

    export { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'

    1. at this point, yarn iosshould work but yarn web gives the error reported here. We need to edit the react-scripts Webpack config node_modules/react-scripts/config/webpack.config.js:

    -- to the plugins of the section marked:

                // Process application JS with Babel.
                // The preset includes JSX, Flow, TypeScript, and some ESnext features.
    

    (at about line 387) add this plugin:

                      [
                        "@babel/plugin-proposal-class-properties",
                        {
                          "loose": false
                        }
                      ]
    

    after that section, create a new section:

                // Process paper specially
                {
                  test: /\.js$/,
                  exclude: /node_modules(?!\/react-native-paper|\/react-native-vector-icons)/,
                  use: {
                    loader: require.resolve('babel-loader'),
                    options: {
                      babelrc: false,
                      configFile: false,
                      compact: false,
                      presets: [
                        '@babel/preset-env',
                        '@babel/preset-react',                
                        '@babel/preset-flow',
                      ],
                      cacheDirectory: true,
                      plugins: [     
                        [
                          "@babel/plugin-proposal-class-properties",
                          {
                            "loose": false
                          }
                        ],
                      ],
                      // Don't waste time on Gzipping the cache
                      cacheCompression: false,
    
                      // If an error happens in a package, it's possible to be
                      // because it was compiled. Thus, we don't want the browser
                      // debugger to show the original code. Instead, the code
                      // being evaluated would be much more helpful.
                      sourceMaps: false,
                    },
                  }
                },