paint-brush
Using Core Node JS Modules in React Native Appsby@aakashns
27,234 reads
27,234 reads

Using Core Node JS Modules in React Native Apps

by Aakash N SJuly 2nd, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

React Native can’t package core Node JS modules into React Native, so you get a runtime error. This makes core modules like crypto modules like that of the Node JS binary unusable from React Native. Fortunately, there’s a solution to this problem, but it takes some work. There is a hacky but simple way to fix this problem with React Native: create a standalone Javascript file that contains a polyfill for themodule and use it within our app. The last few lines of the file contain our code from the polyfills.

Company Mentioned

Mention Thumbnail
featured image - Using Core Node JS Modules in React Native Apps
Aakash N S HackerNoon profile picture

Here’s a problem you might run into while using React Native: Let’s say you want to use the [crypto](https://nodejs.org/api/crypto.html#crypto_crypto) module to create some hashes. It might seem natural to do something like this:

Just import a module and create a hash, right? Right? Nope!

But this doesn’t work, because crypto is a core Node JS module, which means it’s probably C++ code bundled with the Node JS binary, not Javascript. The React Native packager can’t package it[1] along with your app’s Javascript bundle, so you get a runtime error: Unable to resolve module 'crypto'.

This makes core modules like crypto, [stream](https://www.npmjs.com/package/stream) etc. and the thousands of npm modules that depend on them unusable from React Native. Fortunately, there’s a solution to this problem, but it takes some work.

Solution

If you’re familiar the module bundler Browserify, you might know that it allows you to use core Node JS modules like crypto in the browser using polyfills[2]. So let’s try to create a standalone Javascript file that contains a polyfill for the crypto module, and use it within our app:

1. First, install browserify:

npm install -g browserify

2. Create a file crypto-in.js that imports the crypto module and simply exports it:

3. Create a standalone Javascript bundle crypto.js using browserify:

browserify crypto-in.js -o crypto.js

This creates a file crypto.js , which contains some 21,500 lines of code (containing the polyfills). The last few lines contain our code from crypto-in.js :

At this point, it might seem like we’re done, but we’re not! If you replace the line const crypto = require('crypto'); in the app code with const crypto = require('./crypto'); and try to run the app, you get the following error:

Turns out the word require is given special treatment by the React Native packager, and you can’t redefine it to mean something else, which is what Browserify tries to do within the bundle. If you look carefully inside crypto.js, you will notice that it always passes in a custom require function as a parameter and never actually uses the global require. There is a hacky but simple way to fix this problem, explained below.

4. Open the file crypto.js in a text editor, and replace all instances of the word require with something else e.g. reqqq . Make sure it’s unique, so it doesn’t conflict with any existing code and mess things up.

If you save crypto.js and reload the app now, the previous error will go away, but a new error will appear:

The packager isn’t able to find the method createHmac, because crypto isn’t exported properly from crypto.js. If you look at the last few lines of the bundle, you’ll find that this is because we’re setting module.exports not on the global modules object, but on something else that’s passed in as a function argument:

5. To export crypto properly from crypto.js, we need to make a few more changes to the file:

  • Add the statement var crypto; at the top of the file to declare a new variable.
  • Replace the statement var crypto = require('crypto'); with crypto = require('crypto'); to set the outer variable, instead of creating a local variable inside the function body.
  • Move the line module.exports = crypto; outside the function body to the bottom of the file, so that it references the global module object.

After the changes, crypto.js should look something like this:

And that’s it! If you run the app now, it should all work perfectly:

Summary

Here’s a short summary of the steps it takes to use a core Node JS module like crypto (or something that depends on it) in a React Native app:

  1. Create a standalone Javascript bundle containing polyfills for the core modules using browserify (see crypto-in.js and crypto.js).
  2. Replace all instances of the require keyword inside the bundle with something unique e.g. reqqq, so that the React Native package manager leaves it alone.
  3. Export the crypto module correctly from the bundle, by importing it into a global variable, and setting the global module.exports to that variable (see the modified crypto.js).

Warning: The bundle generated by browserify can be rather large in size. For instance, the file crypto.js generated above is over 500kb in size. So keep an eye out for large bundles. Consider using a tool like Uglify to minify the bundle and reduce its size.

References



[1] — https://github.com/facebook/react-native/issues/1871[2] — https://github.com/substack/browserify-handbook#builtins[3] — https://github.com/facebook/react-native/issues/6253

Thanks for reading! I hope you’ve found this article helpful. Feel free to leave comments or suggestions. I would love to know if there is a better way to do this.