React Native and TypeScript – Developing cross-platform apps

Callum GavinTech Stuff12 Comments

react native and typescript

Editor Update: Raygun4JS React Native support is currently in beta. Installation and setup instructions can be found on the JavaScript provider documentation page

There’s been quite a bit of buzz recently about the benefits cross-platform mobile development gains from libraries such as React Native, as well as what TypeScript can bring to improved JS developer experience with static typing. React Native comes out of the box with Flow support, and that looks to be a solid choice too for client-side type guarantees.

If you’re looking into using TypeScript however as part of your wider application codebase, you can use TypeScript with React Native projects to target web and mobile for iOS and Android (and soon-to-be Windows too), with lots of shared code, knowledge and tooling.

Guides to modern client-side development often feature very heavy build tooling setups that can feel brittle and a right pain to get up and running. I got this particular stack up and running relatively easily, and what it brings to the dev experience is simply worth it. For larger projects, the benefits for maintainable and code safety with static typing and the productivity boosts with one shared base framework can’t be overstated either.

react native and typescript

In this Raygun post, I’ll be looking at the tooling needed to set up a new React Native project, convert the code to TypeScript, get the build process working, and finish with some notes about the workflow and the plusses it brings. React Native and TypeScript together you say? Yes, so let’s get on with it!

Creating a new React Native project

Starting from the absolute basics, we’ll install Node (for the JS bundle server), Watchman (a fast native file watcher tool from Facebook), and the React Native CLI to create the new project’s skeleton.

If you already have the above, you can skip to the next section where we’ll convert the project to TypeScript.

Install dependencies for React Native for your OS and preferred target platform:

https://facebook.github.io/react-native/docs/getting-started.html

As an example, for developing on MacOS and targeting iOS, you can run these commands from the directory where you want to create your project beneath:

Your device simulator will then appear and run the Hello World sample.

Converting the project to TypeScript

Here we’ll install the TypeScript tooling from NPM, and alter the project structure for a clean development/bundle workflow. Run the following:

The third command will create a new tsconfig.json file for our TypeScript build config. The fourth one will install a tool to grab type definitions from DefinitelyTyped, required to build the project dependencies. Next, from the project root move the React Native JS source files to a directory beneath it, such as src/, and rename them to their TypeScript extensions. For JSX files, rename them to .tsx, and for non-JSX .js modules, rename them to .ts. Your index.*.js files need a .tsx extension if they’re React Components.

Now we’ll configure TypeScript to build our React JSX syntax files. Add this to tsconfig.json:

Note the noImplicitAny flag above – if you have a greenfield project you can set this to true and gain the full power of static typing for the client out of the box. For larger existing projects this will require some refactoring out of the box, so it’s OK to let the compiler be lenient while you’re getting the build process set up.

Finally, to all your TSX files, add /// <reference path="../typings/tsd.d.ts" /> at the top, to reference the DefinitelyTyped definitions.

Now, with your tsconfig.json in place and your .ts[x] files beneath src, you can run the following:

and the compiler will output valid ES5 JavaScript beneath artifacts/. You can then update the native code to reference the new entry points, such as in AppDelegate.m, so that the jsCodeLocation looks like jsBundleURLForBundleRoot:<a href="http://twitter.com/"artifacts/index.ios">@"artifacts/index.ios</a>" (and the same for Android). Running npm start will have the RN CLI pick up the changes and bundle them up, ready for your simulator or device to load and run them.

Exploring TypeScript with the initial refactor

After running the compile command, you will also see a varying bunch of type warnings – this is a good thing! Your client-side code is now statically typed and the refactoring process can proceed safely

The most impressive thing about the TypeScript compiler is that while refactoring my JS to TS, that mechanical process of fixing the compiler warnings resulted in me picking up many logic, type and code path bugs that were hiding in the JS.

Guaranteeing the sanity of inputs and outputs results in more reliable code that doesn’t fall over at runtime, and therefore doesn’t need a human to diagnose and mop it up in production.

As a general guide, the initial process mostly involves updating code like var foo = 'bar' to var foo: string = 'bar', for both variables, function parameters, module exports etc. There’s also a bunch of nice static type definitions for the React/React Native libraries that the DefinitelyTyped definitions give you, and these are very human-readable and guide you as to what the API expects. Because those preconditions are now listed and discoverable, if you get stuck with the syntax warnings and types are now quickly googleable, speeding up the mechanical refactoring process further.

My favourite language feature in TS is generics – being able to write these in client-side code is a breath of fresh air and leads to much more natural and maintainable abstractions/DRY code. This alone is worth the cost of entry for me.

The documentation for TypeScript is thorough and clear, and for React TSX classes such docs at https://www.typescriptlang.org/docs/handbook/jsx.html provide a good overview of what it can do.

Fully modern development workflow with React Native and Typescript

Once you’ve got your project compiling and running to an acceptable degree, you can easily add a couple of nice tools that make the dev process really smooth and modern. My first recommendation is either Visual Studio Code or Atom, for lightweight IDE goodness.

The former has really tight TypeScript integration (VSCode itself is built with it), including build-on-save, inline Intellisense code hints/squigglies, compiler warnings in the build tool etc. Atom also has an atom-typescript extension, and a solid Vim mode (which I’m a fan of – VSCode’s Vim mode extensions are still under heavy development currently). As of time of writing VSCode feels very natural and jank-free, it Just Works and is very configurable.

Both, however, will use the new type definitions to show the real types of your function parameters and variables when you hover over them with the mouse. No more hunting through massive projects with grep/find-in-files just to find out what types common function expects or returns. TypeScript will give you the correct type the majority of the time with its structural (duck) typing.

After you’ve got the ability to type text into a file and compile it sorted, the next step is fancy modern hot reloading and live reloading support. If you’re not using your editor to call tsc with compileOnSave, naturally running tsc is manual and needs to be automated. A couple of options here are to use the grunt-typescript or gulp-typescript plugins to run on the src directory after detecting file changes, with [grunt|gulp]-watch.

After that step spits out the JS files into artifacts, the bundler can then pick them up and load them onto the device/simulator. By pressing Command-D (iOS) or the R key twice (Android), you can bring up the developer menu and enable Live Reload. In this mode, when the bundle has been re-generated the simulator will automatically reload, enabling a really fast development cycle.

This can be made even faster with the Hot Reload option, which dynamically replaces the changed class/module without causing the app to reload all its JS. This is amazing for designing with the React Native Flexbox implementation as the combination of type hints on FlexStyle and sub-second UI updates make it fast to build a UI. And the best thing with this stack – if you architect your app properly, it’s one code base that is cross-platform and statically typed, leading to far lower maintenance costs and gnarly on-device debugging.

Naturally, static typing is not a panacea for all runtime bugs, but having a sane, designed language certainly helps you a whole bunch up front. For the logic/state/environment/etc bugs that do make it through, Raygun fortunately fully supports both sending runtime errors from the generated JS, as well as native exceptions that occur in the underlying Objective C/Java code.

This combination gives me more confidence in deploying updates and refactoring code, while ensuring end users get their hands on releases in a timely manner, as bug-free and feature-rich as possible.

Keep your software error free with Raygun. Book a demo with an experienced team member to find out how Raygun can help your software team, or start a free trial in moments here. 

Next level software intelligence across your entire stack.

12 Comments on “React Native and TypeScript – Developing cross-platform apps”

    1. Freyja Spaven

      Hi Ken, thanks for your comment. Ben, our front end developer, gave this answer:

      “Raygun should work with React Native.

      I can’t give a definitive yes, not having played with React Native myself and not currently having a provider.
      That said but we do both native mobile (both iOS and Android) plus JavaScript providers so one should “hopefully” be easily created.
      You might even want to use both the JavaScript provider to track js errors, with a native provider for native errors.
      If you go the DIY route, we have documentation for the Raygun API https://raygun.com/docs/integrations/api and anyone is more than welcome to create a provider for Raygun using it.”

      I hope this answers your question! Let me know if I can help further.

  1. Callum Gavin

    Hi Ken,

    Just a bit more info on this – Raygun can support React Native, primarily with Raygun4JS for catching errors in your React components and app logic, and optionally Raygun4iOS/Raygun4Android for catching native errors in the runtime (or vice versa if you want to catch native bugs over JS ones).

    There’s a minor sticking point currently in that Raygun4JS doesn’t support require() importing which would be most idiomatic for RN. If you would like support today, a workaround is available using this approach:

    1. Add it to your project using NPM: npm install raygun4js
    2. Reference and cause the script to instantiate by adding a function that contains code like this:

    const script = document.createElement(“script”);
    script.src = “/node_modules/raygun4js/dist/raygun.min.js”;
    document.body.appendChild(script);

    Raygun.init(‘yourApiKey’).attach();

    That code should be called early on, e.g from your index.ios.js or index.android.js files.

    For catching native errors, you can install Raygun4iOS/Raygun4Android in the usual way e.g with Cocoapods/Gradle, then referencing them in your project files. Following that you can then instantiate the libraries as per usual with their setup instructions (available within the Raygun webapp).

    We do realise this process is a little manual, and we’d like to have a first-class solution for you. If you create a feature request at https://raygun.com/thinktank/forum/2 we can look at creating a dedicated React Native bridge module to simplify this setup process.

  2. Ken Leland

    Callum,

    I have tried what you suggested. React Native complains that document is not defined. I am trying to hack my way through this. Any advice would be helpful.

    Thanks!

  3. Callum Gavin

    Replied on the raygun4js github repository – the provider will require modifications/a fork to support React Native which we have planned for the near term.

  4. Stuart

    Any tips on getting it to run on android when the output directory is changed? For the life of me I can’t figure it out, and typescript refuses to compile to the root directory.

    1. Callum Gavin

      Hi Stuart,

      Generally you want tsc to compile to an artifacts directory, e.g from the root it reads from src/ using the rootDir and filesGlob properties, then outputting to the outDir directory. With those valid ES5 JavaScript files you can then re-point the React Native bridge side to read from the artifacts dir as in jsBundleURLForBundleRoot. Unfortunately we don’t have any experience with the Android side of things and the toolchain can be quite brittle/not respect various edge cases in your environment. We recommend you raise a ticket on the React Native or Typescript GitHub repositories for support on their tools, depending on which is failing.

  5. Pingback: Provider update: Raygun and TypeScript 2.0

  6. Tony

    Have you gotten a chance to actually make use of React Native in the time since this article was written? If so, how well does it handle localization and RTL layout concerns?

    This is something that’s dead simple in native Android, with the ability to specify resources such as strings, styles, font sizes, and layouts, as being specific to locale, screen size, and screen orientation. It’s facilitated with a combination of size/locale folder structures for resources, as well as locale-ambiguous attributes such as marginStart/marginEnd instead of marginLeft/marginRight.

    How is this handled in React? Is localization a bucket of pain once you get into the weeds with this, or is it elegant along the lines of Android native?

    1. Freyja Spaven

      Hi Tony, that’s such a great question and would love to help, we’re a developer tools vendor and integrate with the mobile frameworks themselves, but don’t have any specific experience or guidance that we can provide for developing mobile apps e.g for localization.

      You may like to try posting your question at reddit.com/r/coding, StackOverflow or create an Ask HN post at news.ycombinator.com/news – I hope that’s helpful.

Leave a Reply

Your email address will not be published. Required fields are marked *