React: Boost performance with effective rendering

Content posted here with the permission of the author Payal Bhalerao, who is currently employed at Josh Software. Original post available here

We always enjoy fast and responsive user interface, but generally during fast development performance gets neglected. In case of react app development, We generally rely on react’s virtual DOM, that it will perform diffing and re-render only what is changed.

Yes, it does! But why not to give some hints to skip usless re-rendering, if we can!

Let’s try to understand — What are useless re-renderings ?

In many cases, React component will re-render when it doesn’t have to. When result of next rendering would be same as previous, it’s always better to return and render previous output.

For performance optimization, React offers many higher order components and hooks on which we can rely on.

Two of them are— useMemo() & React.memo()

From useMemo() documentation — 
Pass a function and an array of dependenciesuseMemo will only recompute the memoized value when one of the dependencies (either a or b) has changed.

React.useMemo(
  () => computeExpensiveValue(a, b), 
  [a, b]
);

This optimization helps to avoid expensive calculations on every render.

From memo() documentation — 
React.memo is a higher order component. HOC that can optimize rendering of your component given that, it renders the same output with the same properties.

React.memo(function MyComponent(props) {
  /* render using props */
});

Since both terminologies have the word memo it might sound a bit confusing for people. but, it’s all about React optimization and memoization!

“In computing, memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.”

Difference between useMemo() and React.memo()

useMemo has a different use case than React.memo. It’s not about preventing an entire component from re-rendering, it’s about simply memoizing some output data. Very unrelated in usage.

The only similarity is that React.memo() and useMemo() both involve “if you see the same inputs as last time, don’t do any extra work – return what you had before”, but React.memo is for wrapping up entire components, and useMemo() is for whatever you want to return from the callback.

In this post, We will discuss React.memo() in detail, how it improves the performance & when to use it.

What is Rect.memo()

React.memo is a higher order component.

It’s similar to React.PureComponent but for functional components instead of classes. If your function component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result.

Let’s take one example,

const Counter = ({counter, value}) => {
    return (
        <div>{counter}: {value}</div>
   )
}
export default Counter;
const App = () => {
    const [count1, setCount1] = React.useState(0)
    const [count2, setCount2] = React.useState(0)

    const incrementCounter1 = () => {
        setCount1(count1 + 1)
    }

    return (
        <>
          <button onClick={incrementCounter1}>
             Increase counter 1
          </button>
          <Counter value={count1} counter={'1'}>Counter1</Counter>
          <Counter value={count2} counter={'2'}>Coutner2</Counter>
          // counter2 component should not re-render, 
          // as we are incrementing only counter1  
        </>
    )
}

Every time we clicks on the button, the state of count1 changes causing the App to re-render both counters which is known as useless re-render. However, we expect only the counter1 to be rendered since nothing has changed with the counter2. In reality, both counters get re-rendered.

How can we address this issue?

React.memo is our answer. All we need to do is to wrap our Counter component within React.memo

export default React.memo(Counter);

By default, React.memo will do shallow comparison of props passed to the component. If these props are unchanged, React.memo will reuse the last rendered result and so it prevents the component from being re-rendered.

How to override default comparison

React.memo accepts a second argument, as a comparison function. This makes it comparable to shouldComponentUpdate in class based components.

React.memo(Component, [areEqual(prevProps, nextProps)]);

The comparison function also returns a boolean value that tells React if it should use the previous result of the component instead of calculating a new one.

  • When true, the function component will not be executed and the previous result will be used instead.
  • When false, the function component will be executed like it normally would.

Watch out! This is the opposite from shouldComponentUpdate!

The comparison function is called with the prevProps and the nextProps. This allows complex logic where the current props are compared to the previous props in order to determine if the output would be different or not, and thus remembered result/memo of the component should be used.

const Counter = ({counter, value}) => {
    return (
        <div>{counter}: {value}</div>
   )
}

const areEqual = (prevProps, nextProps) => {
  return prevProps.counter === nextProps.counter && 
         prevProps.value === nextProps.value;
}
export default React.memo(Counter, areEqual);

Performance-related changes applied incorrectly can even harm performance. so use React.memo() wisely.

When to use React.memo()

Only for functional component and when there is possibility of re-rendering with the same props.

As shallow comparison will use object reference, we should use memo() only when — props are not much nested objects, immutability is maintained for objects which we are passing as props.

When we are passing functions as a props to the component, we should use useCallback, which returns a memoized callback.

useCallback() – This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

When NOT to use React.memo()

If the component doesn’t re-render often with the same props.

With useless props comparison.

When props are multilevel nested objects (without maintaining immutability)

Function as a prop, without memoized callback. (As it’s reference get change every-time)

Conclusion

React.memo() is a great tool to memoize functional components. When applied correctly, it prevents component useless rendering when the next props equal to previous.

Profiler is great dev-tool to find the performance issues and to identify whether we need to optimize the component or not.

Improvement Over Android’s Background Processing.

Problems that aroused, since the “Oreo released” –

Whenever we developers needed to execute a long-running operation in the background, we would choose one of the following options :

a) ThreadPools
b) RxJava / Coroutines
c) ForegroundService
d) JobScheduler
e) Alarm Manager + Broadcast receivers

Above options are helpful but sometimes put us in certain situations to write boilerplate code, that should have been handled by the “API / Library itself” (Network Connectivity / Battery Optimization / Memory Availability issues).

Android being a Linux based system it has its own processes to handle like –
a) Scheduling Tasks
b) Memory Management
c) Process Management & so on

As these processes executes, there might come a case where “Android System” may kill / interrupt/ Suspend our Android background thread / background services

Since the release of Oreo , Android has been more focused on battery optimization, memory optimization, user experience. This Restricts developer to perform, background tasks to run only when the application is in foreground or as a foreground service, if in background.

Which some how degrades user experience !

WorkManager For Rescue !

As from the stable release on March 05, 2019 Developers have started using WorkManager features, Soon Developers have come to know what all things can be accomplished without affecting Oreo’s background restrictions on Apps.

WorkManager is intended for tasks that are Deferrable,
i.e not required to run immediately and required to run reliably even for following :

a) App exits
b) Device restarts
c) Network Connectivity Errors
d) Stopped by system (Where we can provide return retry status for the work being processed )


It is an expansion for JobScheduler framework API.
It also lets you observe the state of the work request so that you can update your UI using LiveData.
It also handles Compatibility with different OS versions.
For example, to ensure compatibility back to API level 14, It chooses an appropriate way to schedule a background task depending on the device API level. it might use JobScheduler or a combination of BroadcastReceiver and AlarmManager.

Lets Code

What you’ll learn –
1) Adding WorkManager to your project
2) Adding Work constraints to WorkRequests
3) Scheduling WorkRequest
4) Chaining Multiple WorkRequests

There are a few key WorkManager classes we need to know about –

Worker : Actual work we want to perform in the background.
We need to extend this class and override its doWork() method.

WorkRequest : An actual request object with some work (Worker Object).
We would pass our Worker object, required for creation of our
WorkRequest. While creating “WorkRequest” we can specify things
like Constraints on which the Worker will be running with.

Constraints : A constraints specifies the requirements that need to be met before
WorkRequest be executed. These constraints can be related to
network, battery or storage:

Steps to perform background processing by workManager –
1) Add Dependency
2) Create Worker
3) Set desired Constraints to Worker
4) Is Request Periodic ? if yes –> “PeriodicWorkRequest”
if no –> “OnetimeRequest”, WorkManager Classes.
5) Finally, Schedule it via “WorkManager”.

Adding WorkManager dependency

implementation "android.arch.work:work-runtime:2.2.0" // Current Stable Version

Create a MyWorker class by extending Worker Class –

    class MyWorker(context: Context, workerParams: WorkerParameters) :
        Worker(context, workerParams) {

        override fun doWork(): Result {
            // Define here your work to perform such as
            // Upload images / log files / Rrefresh App config's data periodically

            // Return Result.success() , Result.retry() , Result.failure()  
            return Result.success()
        }
    }

Creating Work Request

// Creating Desired Constraints
// Should run only when device is in charging mode
// should run only connected to network

val constraints:
       Constraints = Constraints.Builder()
                             .setRequiresCharging(true)
                             .setRequiredNetworkType(NetworkType.CONNECTED) 
                                                      .build()

// We can also pass params using WorkerManager's Data Class

val data = Data.Builder()
data.putString("SyncMaster", syncModuleName) 
          
/**        
     if work is periodic 
     (Mostly used for Oreo onward devices due to restriction
      on background services)
*/

var periodicWorkRequest: PeriodicWorkRequest =
                          PeriodicWorkRequest
                            .Builder(MyWorker::class.java,Duration.ofHours(1))
                            .setInputData(data.build())
                            .setConstraints(constraints)
                            .build()

/**
     Else if work is one time task
*/
 
var oneTimeWorkRequest : OneTimeWorkRequest =  
                          OneTimeWorkRequest
                            .Builder(MyWorker::class.java)
                            .setInputData(data.build())
                            .setConstraints(constraints)
                            .build()

/**
    As we have applied the same constraints, both request
    will work only when device is charging or network is connected 
*/   

Scheduling Work –

/**
  Creating a WorkManager object.
  Request can periodicWorkRequest or oneTimeWorkRequest
*/      
 
  WorkManager mWorkManager = WorkManager.getInstance(Context);  
  mWorkManager.enqueue(request); 

Chaining Work –

/**
   Using same WorkManager instance if we need requests to be executed 
   in synchronous fashion, Which can be perform using
*/
              
	        
mWorkManager.beginWith(request1)
            .then(request2)
            .then(request3)
            .then(request4)  
            .enqueue();

 // Requests should be unique either
 // PeriodicWorkRequest  or OneTimeWorkRequest

WorkManager guarantees to schedule requests in a Synchronized manner as specified.
WorkManager will Handle The Rest !!!!!

ObjectBox: Alternative to SQLite for android

Content posted here with the permission of the author Krishnakant Kumar, who is currently employed at Josh Software. Original post available here

How many times do we need to write CRUD operation while building android application? Many times right? For me its like daily job.

Till now I used to use sqlite database. But when I came across ObjectBox , I started liking it. Why? Because it saves me a lot of time in writing bucket of code which I used to spend while writing sqlite db query also it was a bit complicated / confusing while reading those queries. While in case of ObjectBox your queries becomes easy and readable.

ObjectBox is open source, NoSql and a super fast mobile database that persists objects. It  avoids many repetitive tasks and offers a simple interface of data. It is used to easily manage app’s data into local storage.

Now let’s look into Core Classes of ObjectBox

MyObjectBox: Generated based on your entity classes, MyObjectBox supplies a builder to set up a BoxStore for your app.

BoxStore: The entry point for using ObjectBox. BoxStore is your direct interface to the database and manages Boxes.

Box: A box persists and queries for entities. For each entity, there is a Box.

Let’s get deeper and see how to integrate it and develop a simple application.

Note: Here I am demonstrating CRUD application using kotlin but its very simple and similar to integrate it with Java.

Add ObjectBox Dependencies

In the project level build.gradle file add the following

buildscript {
    ext.kotlin_version = '1.3.50'
    ext.objectboxVersion = '2.4.0'
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "io.objectbox:objectbox-gradle-plugin:$objectboxVersion"
    }
}

Open the build.gradle (Module :App) add the following

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'io.objectbox'

Now  create the Entity class Notification.kt that contains the properties id, title, and brief and fileSource.

import io.objectbox.annotation.Id
class Notification {
    @Id
    var id: Long = 0
    var title: String? = null
    var brief: String? = null
    var fileSource: String? = null
}

Lets initialize ObjectBoxApp class by extending android Application class and hold the BoxStore.kt reference which will be needed to execute different data CRUD operations. ObjectBoxApp.kt class will look like this :

class ObjectBoxApp : Application(){
    private var boxStore: BoxStore? = null
    override fun onCreate() {
        super.onCreate()
        boxStore = MyObjectBox.builder().androidContext(this@ObjectBoxApp).build()
    }
    fun getBoxStore(): BoxStore? {
        return boxStore
    }
}

Lets set up BoxStore to create a notificationBox.

val boxStore = ((context as MainActivity).application as ObjectBoxApp).getBoxStore()
val notificationBox = boxStore!!.boxFor(Notification::class.java)

Here, Notification is an ObjectBox entity. And now that we have its Box, we can start insert and retrieving data from the database.

Insert data into notification box

var notification = Notification();
notification.title = "5 Key Mobile App Statistics App Developers"
notification.brief = "James Ewen looks at five interesting stats based on data from the last year. Then he's going to attempt to understand what these trends show, how it will affect monetization, engagement and other app metrics."
notificationBox.put(notification)

Get all record

val notificationList = notificationBox.all

For delete notification

val notification = getPlayerById(id)
if (notification != null) {
    notificationBox.remove(id)
}

 

Conclusion:
A simpler way to handle data. It reduces lines of code and avoids query complexity. It is up to 10 times faster than SQLite.

Happy coding. Feedback is most welcome 🙂

%d bloggers like this: