Easier React Native upgrade with patch-package

Christian Brevik

Anyone who has worked with React Native (RN) for some time know that the core contributors are not afraid of introducing breaking changes. So upgrading from one version to the next can sometimes be difficult.

It became a lot easier when they introduced react-native-git-upgrade, which if you aren't using yet, then you are seriously missing out.

But that just helps you upgrade your project code, what about your dependencies? For example in RN 0.47.0 they removed createJSModules which countless modules (even one of mine) have been overriding in their Android ReactPackage.

Because it no longer existed, the @Override annotation on the createJSModules method meant the module wouldn't compile at all. This effectively broke the dependencies of people who decided to upgrade to RN 0.47.0.

Fixing it is almost too easy. Just remove the annotation, and the module will compile again. But not every dependency I was using for my app had fixed it yet. And that's totally fine, working with open source is something people do voluntary, and a lot of the time life gets in the way1.

At this point we could roll back to the previous version of RN, and philosophize on the dangers of staying on the bleeding edge. But sometimes you just need to upgrade. In my case I needed a bugfix which affected debugging an app.

So, the non-compiling module hasn't been updated yet, but you know how to fix it. One possibility is forking the dependency on Github, add the fix, and point your package.json to that fork.

But that's a bit like using a sledgehammer to crack a nut. Either way, it will probably be fixed soon upstream.

Use patch-package instead

I suggest instead that you use patch-package which will let you patch your dependencies. This is another nice utility from David Sheldrick, who also made the TypeScript-transformer in my other blog post.

This utility will generate a .patch file, which you can commit to your repository. And then patch-package will re-apply the fix everytime someone installs the dependencies, which works with both npm and yarn.

It is quite simple to set up, you install it in your project with:

yarn add --dev patch-package  

Then add the following script in your package.json:

"scripts": {
  "prepare": "patch-package"

Next you can fix the dependency as it is in your node_modules directory, and then create a .patch file.

An example

Let's say we're using react-native-google-analytics-bridge@5.1.0 in our app, which was a pre-0.47 version. The offending file in this case is GoogleAnalyticsBridgePackage.java, on line 36. That @Override annotation is preventing the module from compiling.

We can open that file in an editor, e.g. vim node_modules/react-native-google-analytics-bridge/android/src/main/java/com/idehub/GoogleAnalyticsBridge/GoogleAnalyticsBridgePackage.java, to fix the bug in the installed dependency. In this case I just remove @Override, and add // Removed in RN 0.47.0 instead.

I then save the file, and run yarn run patch-package -- react-native-google-analytics-bridge:

☑ Creating temporary folder
☑ Building clean node_modules with yarn
☑ Diffing your files with clean files
✔ Created file patches/react-native-google-analytics-bridge+5.1.0.patch

As you see, it compares a clean install to your modified dependency, and creates a patches directory, with a .patch file. The name of the patch contains the dependency name, and the version number.

The patch file itself is very readable if you open it in the correct software. I like using Beyond Compare or VS Code:

patch file

As long as the file resides in your patches directory (with the correct name), it will automatically patch the module for both you and your colleagues when you install dependencies:

> yarn
yarn install v0.27.5  
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
$ patch-package
patch-package: Applying patches...  
react-native-google-analytics-bridge@5.1.0 ✔  

If the dependency you have patched is updated later, and the patch cannot be applied then you will get an error:

$ patch-package
patch-package: Applying patches...  
**ERROR** Failed to apply patch for package react-native-google-analytics-bridge

In that case you can either add/fix a new patch, if the thing you wanted fixed isn't in the new version. Or delete the patch file if it is.

A simple solution

What I like about patch-package is that you do not depend on external repositories for minor fixes. You're still installing the official dependency, with all the benefits that entails.

And it is good that it doesn't require any extra steps from your team members. Fix it once, and it happens automatically from there on.

Of course, this utility will also work for non-RN projects. Pretty much any project that installs npm packages can use it.

I hope more people will find this useful, as it has helped me a lot!

  1. Open source does not work in isolation. If a module isn't working, submit an issue, and then try to fix the problem yourself. If you fix the problem, submit a pull request. If you can't figure out the problem, update the issue with details of your investigation into the problem. The people maintaining your dependencies are not paid to do so, so pay them back with a bit of your own time.