Coding / Programming Videos

Post your favorite coding videos and share them with others!

How an Android developer dived into React Native and survived

Source link

Positive parts

Architecture

Let me start with architecture. It was always a painful part of Android development as Google seemed to not worry about adequate recommendations in this area. They started to do it recently and even build Architecture Components library pack which is pretty cool but do not cover all the blocks of real-life application (yet?)

On the other hand, Facebook proposed Flux architecture as addition to React some time ago and this is good I must say. At least Redux implementation of it (there should be a more profound difference between Flux and Redux under the hood but let me skip this 🙂 ). It takes some time to dive into Redux but after that it became quite comfortable to use it.

Redux has several main components that are instruments to build more or less flexible and maintainable codebase and prevent messed up connections between app parts. First, you have a Store which represents the state of the application in any moment of time. Like really. If you cook it right you may get a dump of app state by grabbing a store (that’s how redux-persist works but we’ll talk about it later). You may listen to store changes but do not modify it directly. You should notify about events that should affect store only by sending (dispatching) Actions. These are plain JS objects that may contain some additional payload in addition to an action type. So if you’re developing (yet another) Todo app you will for sure have some action with type “ADD_TODO” and payload that contains todo title and maybe notification time. As actions are plain objects they don’t (and shouldn’t) contain information on how app’s store should change, they only tell what’s happened. Reducers are the blocks to modify store according to actions. These are just functions that get current app state plus received action and return modified state.

Результат пошуку зображень за запитом "redux"
Something like this (randomly googled image)

So, data layer built upon a combination of these three blocks is solid I must say. UI components can be connected to store and receive all the necessary updates to modify presentation accordingly. This gives a lot of opportunities. For example, we implemented hot app locale switch almost effortlessly.

No lifecycle callbacks pain

BTW, talking about locales. Historically Android components like Activity can be recreated literally at any moment. And they call it configuration change. Screen rotation, system locale change are some of those triggers. And overall Activities and Fragments lifecycle is a complete mess. But you gonna understand it to write apps that work correctly and save data somehow to restore it after recreation.

I saw this image in some book, but copied it from here. Just to scare you.

React Native is much easier in this area. You have to understand a lot fewer callbacks to start to write working apps. These are mostly componentDidMount and componentWillUnmount. And you are not forced to handle screen rotation unless you need it (when writing regular Android apps you can make Activities to act the same way by adding a relevant parameter to AndroidManifest, and I’m starting to think this should be the default behavior. Long live to ViewModel from Architecture Components which survives screen rotation). Data and even navigation state (if you’re using a right navigator) can be easily saved to survive process death using redux-persist. It is useful for a lot of things, like the next point.

Data caching

This one is related to architecture a little bit. Let’s assume you are developing yet another weather app and want to cache the weather for the next week locally for the user to see it being in offline. If you are an Android developer you know how to solve it. You get SQLite (you say Realm/ObjectBox? Okay man, just don’t spill your smoothie on me). Even though database is not necessarily a bad thing, for simple cases it may be overkill. Using React Native and Redux with redux-persist we just make our stores be saved to AsyncStorage in the background and worked with them the same way as with simple in-memory stores. Worked like a charm. Nothing about manual data mapping and SQLite queries.

JS and (mostly) JSX

I always heard bad things about JavaScript. Well, this language has a lot to complain about, but actually, it’s not that bad. At least in its latest ECMAScript revision. Map-like objects, spread operator and destructuring are quite funny. And functions with functions as parameters and return functions are pretentious. It looks neat when you get used to it.

Of course, weakly + dynamically typed language allows you a lot of dangerous things, but it may even help sometimes when you iterate fast 🙂 For example, we had some mock data (lists of objects with numeric id field) when started development. But when real data was ready ids became strings. This should break everything in “normal” languages, but with JavaScript, we just got warnings from prop-types. Of course, we fixed typings but it didn’t prevent us from doing our urgent tasks (that’s true unless you’re trying to do arithmetics with ids. But you completely screwed up doing this in any case).

And JSX is my love. If you’re not trying to write all the logic in render() function it is splendid. Here’s a brief example. You happened to need some short list of elements (let’s say up to 10). If you’re an Android developer you have 2 options usually. You can go with RecyclerView (which is too much for lists with such small amount of static elements) or you can try to add Views programmatically to ScrollView (which requires quite a lot of boilerplate code). But that’s how you can do it with React Native:

<View>
{someList.map((someObject, i) => 
<Icon key={i}
name={someObject.name}
size={18}
color="#8bc34a" />)
}
</View>

Right. You just map your list of data to React Native components (don’t do this with long lists, go learn FlatList).

Another fancy thing is rendering parts of layout only when some clauses are true:

<View>
{isDataAvailable() && <DataRenderer data={this.props.data}/>}
</View>

Or slight variation:

<View>
{pojo !== null ? <DataRenderer data={pojo}/> : null}
</View>

Android’s DataBinding can do something similar by adding clauses to visibility param in xml, but that’s far from such JSX elegance.

So generally I enjoyed building simple UI with React Native more than with Android.

Cross-platform development

Yeah, this is definitely a good part of React Native. Even though you cannot have 100% code reusing between platforms, it’s a deal-breaker when you have limited resources. We successfully made apps for both Android and iOS. That was definitely not twice as quick as developing native apps separately, but still quicker. You just have to be ready to dabble with Xcode and Objective-C (which was definitely more painful than React Native itself).

Writing platform-specific code is easy-peasy when we’re talking about JS part. In simpler cases you can just make checks on the fly like this:

<KeyboardAvoidingView
  behavior={Platform.OS === 'ios' ? 'padding' : null}>
...
</KeyboardAvoidingView>

When you have more code related to only one platform or want to have dramatically different UI on Android and iOS you create two files with names filename.android.js and filename.ios.js and then import this module as filename. No drama here.

I haven’t had a chance to develop native modules in Java/Obj-C so can’t say anything about its quirks but documentation looks straightforward. At least if you are a native developer.

Disappointments

Of course React Native is not ideal. I have some things to complain about. So let’s get started.

Performance

React Native is faster than Cordova and all that WebView stuff. It’s a fact. But still not as fast as real native apps. Which makes sense if remember about JS bridge overhead. I don’t want to dive into performance inspections here but of course JS Bridge instantiation and communication with UI thread takes some time. Here is a nice article about how it works under the hood. It has “part 1” in its name, but unfortunately it is the only part. Still good tho. And another one.

Actually, I was ready for cross-platform development penalties and performance is one of them. It’s not a disastrophy if you do it right (like use FlatList instead of ScrollView) but don’t expect to compete with “real native” apps.

Tooling

Android Studio and its tooling iterate fast as well as build tools, so can’t say they are super stable now, but they still more stable than React Native tooling.

I’ve used VS Code for writing the code. Besides integrated terminal quirks and build-in git tools (ugh, this jumping after reverting a changed line of code), it’s nice. But I still don’t really understand how to debug the code with or without it. Maybe I just need to use different approaches here, but on Android I can just put a breakpoint on a line of code, attach a debugger to the app, code execution will stop on exact line I want and I will be able to inspect let’s say all the variables I’m interested in. And I don’t need to dance around to make it work. Shame on me, but I’ve used React Native Debugger and console.log to do such things. It was a pain on Android emulator because the app worked so slow with the attached debugger. Shame on me again, but I, Android developer, used mostly iOS simulator to debug the app.

Errors inside the emulator/simulator are a joke. Maybe I’m just used to Android Studio logcat and verbose stacktrace, but there should be better ways to show errors. Yes, I can attach a debugger and see some errors in JS console, but read the previous paragraph about app speed in this state.

Sometimes errors say “Catch me if you can”

I regularly got errors when tried to build or launch the app. And according to SO/Github issues most of such problems can be resolved by removing the node_modules directory and reinstalling dependencies (lucky you when clearing Watchman cache helps in your particular case). It’s just uncomfortable.

ESLint doesn’t understand if you’re trying to import a module with index.android.js and index.ios.js (eslint-disable-line to the rescue):

You better not to try using native libs autolinking. It was going me crazy when I launched linking two times and whole Android project messed up as all the dependencies in Gradle files and required code in java classes were added twice. Cleaning this up is not funny at all. So I ended up doing linking manually all the times.

And frontend guys, how do you configure code alignment to be identical on all devs machines? I still don’t understand it 🙂 It’s so easy with Android Studio, you just push codeStyleSetting.xml to your git repo and voila, everyone can press CMD+Shift+L and don’t be afraid about all the file be rearranged. I’ve played with Prettier and ESLint and finally gave up.

This can be a long story, I regret a didn’t make notes while fighting with all the problems I stumbled upon. There were Xcode problems as well but they are not really React Native problems. But at the end of the day after all that stuff I’m just saying that Android Studio and Gradle are pieces of cake when doing the comparison 🙂

Libraries

Luckily there are a lot of cross-platform libraries for React Native. But while they support twice as many platforms they are twice as buggy at the same time. And from time to time there is no library you need and you have to go and implement it by yourself. Let me point only on a couple of problems I struggled.

react-native-maps had initial location property broken on iOS. It’s fixed now but I had to workaround it by myself somehow.

react-native-material-kit still uses Gradle syntax removed from latest releases, so I’ve added its fork as a dependency for now.

I wasn’t able to found anything comparable to phonegap-launch-navigator, so I had to write separate logic to launch maps apps for Android and iOS. Luckily I did it with JavaScript only. Boo iOS for not having general URI to launch any navigation app.

Googling didn’t help me to set up images loading with correct caching (even though React Native must be using Fresco library internally). Nothing like Glide.

Source link

Bookmark(0)
 

Leave a Reply

Please Login to comment
  Subscribe  
Notify of
Translate »