The title is exciting, isn't it? The purpose of this blog is not to explain these libraries, very good documentation for them is already available. This blog will explain why the native app developer needs these libraries and how the developer experience changes when the need for such libraries is understood.
When you finish this blog series, I promise, you will learn a new way of writing your mobile apps, where you have a lot of predictability, testability, and better developer experience.
The concepts explained in this blog are platform neutral; both the iOS and Android development community can read on.
Let’s Understand the Need
Mobile apps are evolving fast; many of them perform more complex work than when they were conceived. With changing needs, are our existing app development architectures flexible enough?
Current architectures (MVC, MVVM, MVP, cleanAndroid, and so on) are built on an assumption that changes are always minimal and incremental, like adding one or more UI controls to the UI screen. But often, changes are non-incremental and sometimes disruptive, such as
- Adding completely new information to an existing screen- for instance, showing possible add-ons on the order confirmation screen.
- Introducing a new screen in the existing flow- for instance, introducing the user's frequently-bought items before user pays for the order.
How do we react to such app needs? We often rewrite the mobile app screens, and yes, more or less, we end up changing many classes across the whole app!
Why? When we add new information to the existing screen or add a new screen in a mobile app flow, we are altering the application state. Often, altering the app state sends rippling changes through the app code, and generally, we end up editing a good number of classes.
What Is Application State?
App State is the UI data models used by the app, UI views that reflect the portion of them and user actions, data from APIs, and data sources such as SQLite- content providers update them. You may not refer to them with the name App State, but certainly, every app has something like “App State.”
How Are We Updating the App State?
- No consistency: Often, trivial modification of the state results in disruption, as we don’t have a consistent way to handle the UI state of the app. It is the developer's discretion to decide how to update the app state. Often, the same sub-state gets updated by different code blocks.
No single source of truth: The application UI state is stored in different objects, and often they are defined according to the needs of the screens. The data gets duplicated across many objects.
As the app grows, so does the complexity of handling the states. For example, in the above picture, if the age is updated, every screen state that has the age has to be updated.
- Multiple communication paths: In typical app code, changes are communicated two ways: from the code blocks to the data models, and from the data model to the components.
If you think the above picture looks messy, the cruel truth is that most of our native app code looks exactly the same.
Now that we understand the issues, let’s find out the solution explore — is there any better way?
It starts with the below principles:
- Have a single source of truth for the app state.
Let’s not have every screen have its own state. Instead, every screen should work with the same object, the app state. The state can have sub-states. The composition of sub-states makes the app state; for example, in an ordering app, sub-states could be the user, user profile, offer, cart, order, last orders, etc.
2. State is read-only.
- Not every function in the app can update the state. Instead, have a consistent mechanism to update the states.
- The initiation of a state update should be explicit, using an object that describes what happened. Redux calls the object "Action."
- Neither the views nor the network callbacks will ever write directly to the state.
- All changes are centralized and happen one by one in a strict order. No race conditions!
3. A pure function changes the state.
State changes are made by the functions that take the previous state and an action and return the next state. These functions do not change the previous state; they create and return new state objects instead of mutating the previous state. Redux calls these functions "reducers."
What about unit testing such a mechanism?
Absolutely necessary. In order to ensure unit testability, such a mechanism should be a pure function, devoid of native app intricacies such as "context" in Android, or "viewcontrollers" in iOS.
React to State Changes
The states are updated, now how are the components updated with the app state changes?
Before understanding that, let’s understand components. Components can be a portion of a native app page, or even a complete app page. These are Fragments and Activities components in Android; Viewcontroller and Views are components in iOS.
In order to get updates, the components subscribe to the state changes.
When the app state is updated, the subscribed components are notified of the state changes. It is up to the components to react to events, either by updating the UI, taking some action, or simply ignoring the event.
Simple, isn’t it? Remember, in typical native app code, we have the series of callbacks instead, knocking on the door of callback hell.
In the Next Post…
Now that we understand and have learned the concepts, we will look deeply into the Redux and code examples in the next post, where you will start appreciating how Redux changes the developer experience.