How it works...
We have prepared a (hidden) component that responds to orientation changes by dispatching an action to update the store, and we know how to code a component that will use the device information. Our main page could look as follows:
// Source file: src/adaptiveApp/main.js
/* @flow */
import React from "react";
import { View, StatusBar } from "react-native";
import { ConnectedAdaptiveView } from "./adaptiveView.connected";
import { ConnectedDeviceHandler } from "./deviceHandler.connected";
export class Main extends React.PureComponent<> {
render() {
return (
);
}
}
If I run the app on a (simulated) Nexus 5 device in portrait mode, we'd see something like the following:
Our device is recognized as a handset, currently in portrait (vertical) orientation
Rotating the device would produce a different view:
When the orientation changes, the store is updated and the app re-renders itself appropriately
In our design, components never use the Dimension API by themselvessince they get the device information from the store, testing the components' behavior for different devices and orientations could be done functionally, without needing to mock anything.
Using promises instead of error first callbacks
Now, let's start considering several techniques that will come in handy when writing services.
Node runs as a single thread, so if every time it had to call a service, or read a file, or access a database, or do any other I/O-related operation, it would have to wait for it to finish, then attending requests would take a long time, blocking other requests from being attended, and the server would show a very bad performance. Instead, all operations such as those are always done asynchronically, and you must provide a callback that will be called whenever the operation is finished; meanwhile, Node will be available to process other clients' requests.
There are synchronous versions of many functions, but they can only be applied for desktop work, and never for web servers.
Node established a standard that all callbacks should receive two parameters: an error and a result. If the operation failed somehow, the error argument would describe the reason. Otherwise, if the operation had succeeded, the error would be null or undefined (but, in any case, a falsy value) and the result would have the resultant value.
This means that the usual Node code is full of callbacks, and if a callback itself needs another operation, that means yet more callbacks, which themselves may have even more callbacks, resulting in what is called callback hell. Instead of working in this fashion, we want to be able to opt for modern promises, and, fortunately, there is a simple way to do so. Let's see how we can simplify our code by avoiding callbacks.
Testing and Debugging Your Mobile App
In this chapter, we'll be looking into the following recipes:
- Writing unit tests with Jest
- Adding snapshot testing
- Measuring test coverage
- Using Storybook to preview components
- Debugging your app with react-native-debugger
- Debugging in an alternative way with Reactotron
Spreading and joining values
A new operator, ..., lets you expand an array, string, or object, into independent values. This is harder to explain than to show, so let's see some basic examples:
// Source file: src/spread_and_rest.js
let values = [22, 9, 60, 12, 4, 56];
const maxOfValues = Math.max(
...values); // 60
const minOfValues = Math.min(
...values); // 4
You can also use it to copy arrays or concatenate them:
// Source file: src/spread_and_rest.js
let arr1 = [1, 1, 2, 3];
let arr2 = [13, 21, 34];
let copyOfArr1 = [
...arr1]; // a copy of arr1 is created
let fibArray = [0,
...arr1, 5, 8,
...arr2]; // first 10 Fibonacci numbers
If you apply the spread operator to a string, the effect is to separate it into individual characters, much as if you had used .split(); for instance, console.log(..."JS") shows ["J", "S"], so this case isn't particularly interesting.
You can also use it to clone or modify objects; in fact, this is a usage we're going to meet again later, in , Expanding Your Application, mostly when we use Redux:
// Source file: src/spread_and_rest.js
let person = { name: "Juan", age: 24 };
let copyOfPerson = {
...person }; // same data as in the person object
let expandedPerson = {
...person, sister: "Mara" };
// {name: "Juan", age: 24, sister: "Mara"}
This is also useful for writing functions with an undefined number of arguments, avoiding the old style usage of the arguments pseudo-array. Here, instead of splitting an element into many, it joins several distinct elements into a single array. Note, however, that this usage only applies to the last arguments of a function; something such as function many(a, ...several, b, c) wouldn't be allowed:
// Source file: src/spread_and_rest.js
function average(
...nums: Array): number {
let sum = 0;
for (let i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum / nums.length;
};
console.log(average(
22, 9, 60, 12, 4, 56)); // 27.166667
If you are wondering why I called arguments a pseudo-array, the reason is because it looks somewhat like an array, but only provides the .length property; see more at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments. In any case, you won't be dealing with it, thanks to the spread operator.
Packt is searching for authors like you
If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.
There's more...
With what we have done here, you may realize that you have all you could need to handle state, for any application size and you would be right! You could set up the general state for your whole application in the App component (remember we mentioned that App would have more responsibilities?) and you would be able to do the following:
- Pass it down to components by using props
- Update it in answer to events that the components send
This is a perfectly valid solution, and App.state could have all sorts of data for the whole page. App could handle, for example, calling a web service to get the regions for a given country, storing the results in its state, and passing it to our components so that they can be rendered. In our hardcoded version, RegionsInformationTable had the list of countries (where did it get it from?) and handled the requests for regions (by returning hardcoded data). In reality, as we'll see later in this book, getting this sort of information from a server would be handled differently, and at a higher level: RegionsInformationTable would handle rendering the table and leave data gathering to another part of your solution.
Even if you passed web services handling to App, as your application grows in size, this sort of solution may become unwieldy because of the number of data fields you may have to keep track of. We'll find a better scalable solution for this in ,