Goto maarten hus header

The Post-MVC Age

For a long time the Model View Controller (MVC) paradigm was the gold standard to build applications. Now it seems MVC’s star is dying in the JavaScript universe. Libraries and frameworks such as Angular 2.0, React, and Ember are embracing a new paradigm centered around components. A component wraps the Model, View and Controller into a single entity, violating MVC conventions at every turn. Ideas such as Flux and Reactive Programming have changed our way of thinking about an application’s state. They have radically departed from the principles of MVC in the way entities communicate, and how state is changed and maintained. At the end of this talk you will know what Flux and Reactive Programming are and how they differ from MVC.


Introduction

My name is Maarten and I’m a developer for a Dutch company called 42. We mostly work on Java and Angular applications. My presentation is called “The Post-MVC Age.” I will take a look at component architecture, the new way to do applications.

Here’s an example of a Google Maps component. You can see that there are four cities: San Francisco, Amsterdam, New York, Tokyo. These are Google Maps places, and there’s a map marker at the center of the city. When you hover over it you see information about all those four cities. The code behind a Google Map is relatively simple.


<google-map latitude="37.77493"
            longitude="-122.41942"
            fit-to-markers>
  <google-map-marker
      latitude="37.779"
      longitude="-122.3892"
      draggable="true"
      title="Go Giants!">
  </google-map-marker>
</google-map>

You can create a custom HTML address in the Google Polymer Framework. In this case, a google-map element, and google-map-marker element. And you control the custom element and assign their codes (in this case, the longitude and latitude, which determines the center of the map). To add a google-map-marker we will simply set longitude and latitude, and we make it draggable. This gives you a Google Map.

We can also listen to events:

var map = document.querySelector('google-map');

map.addEventListener('google-map-click',   
  function(e) {
    alert('The user clicked on the Map!');
  }
);

We can send data input code via its attributes. We can listen to what is output by adding event listeners. That’s the Polymer way to do it.

Characteristics of Components

A component is isolated. You can put multiple components, multiple instances of the same component, on the same screen, without them interfering with each other. In the example above, we had four Google Maps components, and they could be controlled separately without them interfering with each other.

A component has very clear input and output semantics. The only way to manipulate a component is via the inputs that it provided for you. The only way a component can manipulate the outside world is to very clearly define the output semantics. In this case, the inputs are the longitude and latitude, and the output was the event listener.

A component is declarative. We saw the Google Map component in action, and it was declarative. It was easy, by looking at the code, to know what the component was doing.

A component is composable. You could take multiple smaller components–for example, a button component and an input component–and combine them to create a search component. We can use components like LEGO blocks, to define components in terms of other components. And that’s a very powerful mechanism.

These characteristics make components very easy to reason about. Because a component is isolated, when you see it on the page/code, you know what that component’s going to do. And because a component has very clear input and output semantics (i.e., it does not manipulate the outside world), it’s very easy to understand its place in your application.

To conclude: components are pretty awesome.

Building Applications with Components

I’ve created a very simple “Todo” application in Angular 2.0 (you can order Todo, you can cross them off). At the bottom there’s a filter, and there you can toggle the component’s state, the Todo state, that you want to see. For example, if you press Active, you only see the components you need to do, and when you click Completed, you only see the Todos you’ve already done.

This app was built from the following components. I’ve represented this as a tree (that’s the funny thing about a component-based application, that it is a tree, like normal DOM elements would be on the page). At the very top is the TodoApplication, called “root component”. It’s either directly or indirectly the grandparent of all other components. This is an example of the composability. This entire application is one single app component.

On the right (see video), we see this TodoList component that has N number of Todos which are currently on the screen. And we have an AddTodo component, which is an input element, where you click Enter on it, it will add a new Todo. And we have the ToggleAll component. When you click that, you instantly check off all your Todos.

On the left, we have the Filters component, which has three Filter components, all active and completed.

If you take a look at this tree (or any tree which represents a component-based application), you might wonder:

  1. Where does the state live in this application (e.g., where are all the Todos stored, where do you store which Filter is currently active)?
  2. How do all these components communicate with each other?

For 1, instead of saying, “Where does the state live? Where do we put the current active filter?”, we could say, “Let’s put the current active filter in the Filters component” because that’s the place they all have in common. They have the same parent.

Get more development news like this

But then you realize that the active filter is used inside of TodoList. And in that component, it is determined which filter to apply. Then you might argue to put the thing in the TodoApplication root component, because that’s the common parent over these two components. I put it in the TodoApplication, all the way at the top.

But the answer is not very clear. You can make the case for each of those three components, and it would probably be a valid case (this is not a very satisfying answer). Later on in my presentation I’ll show you a way to make this question easier to answer. But for now, it’s not very clear where the state should live inside of a application tree.

For 2, “How do components communicate?”, we can ask ourselves: “How do filters change?” I chose to put the filter all the way at the top, in the TodoApplication. When you click Completed, All, or Active, in some way, I need to communicate that all the way to the top, because that’s where the data is stored. When Completed is clicked, I need to send it to its parent. And when Filters has it, it needs to send it up one level to TodoApplication.

Because a component cannot communicate to another component which is not directly related to it. You can only go from the child to the parent, from the parent to the child. That means that communication between components is a hassle. The more distance between the component in which you want to communicate with (for example, here you want to communicate from the blue component, all the way to the red component), you first need to go all the way to top, and then all the way to the right.

These two questions highlight two weaknesses of this architecture. It’s not always clear where the state goes, and communication between components are not directly related to each other. We can mitigate those weaknesses by taking a look at Redux.

Redux

Redux, a library created by Dan Abramov, is the most popular implementation of an architecture called Flux. Flux was invented at Facebook, and it’s a way to do unidirectional data flow. Flux is more of an idea, and Redux is an implementation of that idea (and since it’s the most popular one, I’ve chosen to focus on Redux).

Behind Flux and Redux we still have our components tree, but we’re going to put another concept next to that tree, called the store. Store provides all the state to the component tree. We no longer store our state in components; we store every bit of state of this application in one single variable, in the store.

Whenever an action occurs (for example, we add a new Todo, check a Todo, toggle all Todos, or check on the Filter), an action is sent to the store. That manipulates the store, and then we send the state to the component tree again. If we look at it slightly differently (see video), you can see where the unidirectionality comes from.

The store provides a state to the component tree, the component fires actions, the actions manipulate the store. And this will go around and around in a single loop, but always in one direction. That’s why it’s unidirectional.

Redux and Flux give us two nice answers to our questions:

  1. All states simply live in this store. We no longer have to worry about where in our component tree we store state.
  2. All events route to the store. We no longer need to communicate between two components directly. We simply send all events to the store, where the state is manipulated.

Let’s look at some actual Redux code using a counter application. There’s two buttons: - and +, and the number in between them. When you click +, the number increases. When you click -, the number decreases. Very simple.

In Redux, it’s common to define all of your actions as const definitions.


 // Defines all possible actions as const's.
const INCREMENT_COUNTER = "INCREMENT_COUNTER";
const DECREMENT_COUNTER = "DECREMENT_COUNTER";

/*
   Action creator functions,
   functions that create Redux actions
*/
function incrementCounter() {
  return {
    type: INCREMENT_COUNTER
  };
}

function decrementCounter() {
  return {
    type: DECREMENT_COUNTER
  };
}

We have two actions, INCREMENT_COUNTER and DECREMENT_COUNTER. We also have these two functions, incrementCounter and decrementCounter. And these are what Redux calls action creator functions. They create actions that we can send to the store.

An action is always an object that has a type property. And the type property identifies the action. In this case, whenever we call the incrementCounter function, we get a simple object back saying which type of action it is.

Next, we create a store. First we set the initialCount to zero. Then we say Redux.createStore. And there’s a second argument that takes our initialCount, and as a first argument, it takes this counterApp function.


 // The initial count starts at zero.
const initialCount = 0;

// Create the store with the reducer and the initial state.
let store = Redux.createStore(counterApp, initialCount);

// The reducer for the counter application.
function counterApp(state, action) {
  switch(action.type) {
    case INCREMENT_COUNTER:
      return state + 1;
    case DECREMENT_COUNTER:
      return state - 1;
  }

  return state;
};

The counterApp function is called a reducer. The reducer is a function which states the old state, and an action to apply to that state. And there are a couple of rules with the reducer:

  1. The reducer must be pure: we can never manipulate the old state directly; you always need to create a new copy.
  2. Whenever you encounter an action that you don’t know, you simply return the state.

In the reducer, we handle our two action types: INCREMENT_COUNTER and DECREMENT_COUNTER. When we encounter increment, we simply add one; when we encounter a decrement, we decrease it by one. For example, if our counter has a state of five, and we send the increment action, it will be five plus one: six will be the new state. This is a very simple Redux application.

Redux is framework-agnostic. You can use Redux in React, Angular, or any other type of framework. But it’s more geared toward React. This was a very simple application, where the state was an integer. I want to make it more complex by adding the possibility of dynamically added counters, and dynamically removing them. I want to do the removeCounter first.

Let’s take a look at the initial state.


 // Initially there will be one counter
const initialState = {
  nextCounterId: 1,
  counters: { 1: { count: 42, counter: 1 } }
};

function removeCounter(counter) {
  return {
    type: REMOVE_COUNTER,
    counter: counter
  }
}

// This case is inside the Reducer
case REMOVE_COUNTER:
  var nextState = Object.assign({}, state); // Copy state
  delete nextState.counters[action.counter];
  return nextState;

This time it’s not number, it’s an object. This object has two properties.

First, a nextCounterId, which you can think of as an auto-increment ID on an SQL table. It’s meant to increase each time a new counter is added, we have unique key.

The second property is called counters, a key value pair in which the key is the ID of the counter, which points to the counter object. In this case, we have a single counter object which starts at 42.

Then we see the removeCounter action creator. But this one is different: it one takes an argument, the counter. And that’s the counter that we want to remove.

When you have these actions, you always have a type property that you can add as many other properties as you need for your action. And in this case, it’s the counter. Somewhere inside the reducer, we have the REMOVE_COUNTER. And there I do this little trick with Object.assign to create a copy of the current state. Remember, you cannot manipulate the existing state. Then we simply take the counter from the action, and we delete the key and the key value by our map. And then we return the state, and then we have remove the counter. If you call removeCounter with one, then the count 42 will be removed. Then we have createCounter, and it does not have parameters, because we don’t know which counter we are going to create beforehand.


// Initially there will be one counter
const initialState = {
  nextCounterId: 1,
  counters: { 1: { count: 42, counter: 1 } }
};

function createCounter() {
  return {
    type: CREATE_COUNTER
  };
}

// This case is inside the Reducer
case CREATE_COUNTER:
  var nextState = Object.assign({}, state); // Copy state
  nextState.nextCounterId = state.nextCounterId + 1;
  nextState.counters[state.nextCounterId] = {
count: 0,
     counter: state.nextCounterId
  };
  return nextState;

In the reducer, again, we create a copy, and then we increment that nextCounterId manually, that the next time we have a unique ID. And then we simply take that previous ID, and assign it to a new counter object, which starts counting at zero. And it knows which counter it is, because it has the counter key stored in.

Then we have incrementCounter.


// Initially there will be one counter
const initialState = {
  nextCounterId: 1,
  counters: { 1: { count: 42, counter: 1 } }
};

function incrementCounter(counter) {
  return {
    type: INCREMENT_COUNTER,
    counter: counter
  };
}

// This case is inside the Reducer
case INCREMENT_COUNTER:
  var nextState = Object.assign({}, state); // Copy state
  nextState.counters[action.counter].count += 1;
  return nextState;

You also need to give a counter ID in the action creator, so we can identify which counter we need to increase. Again, we copy the state, and we pluck the counter from the initial state, and we increment it by one. Decrement is much the same, except it decrements the counter by one.

So what we see here is an example of a more complex Redux application. It’s customary to put your state in these objects–in this case, initialState. You may think, “if we have many actions, the switch statement will get huge”… and that’s true. But we can create multiple reducers for the same application. We can split it up based on the domain entity that we’re going to manipulate.

Benefits of Redux

The benefits of Redux include having a very clear answers about where to put the state, and how to communicate with components.

Also, Redux makes Universal JavaScript easier. Universal JavaScript is the art of rendering your application on the Node.js server, and sending it back to the browser, which makes your application render faster.

This is easier in Redux is because all your states are located inside a single variable. You only need to set that variable once, and then give that to your component tree. Then you get your UI, and you can send it back to the browser. If we contrast that with having states located in multiple separate components, we would have to do more work to get those components and assign the state correctly. You would have to write more Boilerplate code. That’s the first benefit.

Redux has a surprisingly good developer experience story. Here is another counter example, but this time with the development tools displayed on the right. (see video) What we see here is that each time that we preform an action, we see the action occur on the right, in black bar. And we can even click on those actions, to pretend they never happened.

You can commit states, and you can load external states from JSON. If your customer says, “I found a bug in your application,” you could ask them to send the current state, and you can load it in your local development environment and see exactly what they saw.

Cons of Redux

There is a learning curve. You have to learn about reducers, pure functions, immutability, thunk, saga. It’s a broad framework: it’s not that difficult to learn, but there is a slight learning curve.

JavaScript is not immutable by nature. JavaScript allows you to change variables. If by accident you do something in your reducer that accidentally changes the state, then your whole application will fall down, because Redux does not accept you mutating the state. You have to be disciplined when writing your reducers, to make sure that you don’t accidentally manipulate state.

Luckily, there are libraries like Immutable.js by Facebook, which gives you an Immutable collections library. In this case, we have map and list, they represent objects and arrays. Whenever you perform an action, or Map, or a List, you’ll always get a new copy. You never modify the existing object. You can use Immutable.js to mitigate this weakness.

But using this library creates a little overhead. And if you have to communicate with some third-party library which expects normal objects, then you would have to do some plumbing to give those third-party libraries normal objects instead of Immutable Maps or Immutable Lists.

Reactive Programming

You can use reactive programming to supercharge your components’ asynchronous event handling. It’s really powerful for that.

What is reactive programming? In “normal programming,” or passive programming, when you have two components, one component is in charge of another component.


Car.prototype.turnKey = function() {
  this.engine.fireUp();
}

In this case, we have a Car, and we have an engine, and the engine starts whenever the car key is turned. In a sense, the engine component is not in control of its own destiny. It always gets told when to start. The Car object has an engine property, and it fires up the engine whenever the key is turned.

Reactive programming inverts this relationship:


Engine.listenToKey = function(car) {
  car.onKeyTurned(() => {
    this.fireUp();
  });
}

Now the Engine knows when it fires up. And the car does not know that it manipulates the Engine via callback mechanism. Whenever the car.onKeyTurned event is triggered, the engine fires itself up.

And this matches to the benefits of components. If you write your components reactively, your component is in control of its own destiny, and you can easily understand how your component reacts by looking at the component source. It will tell you in its source when it gets triggered, manipulated, or does some action.

RxJS

Callbacks are a very poor mechanism. They are relatively low level. There are libraries, such as RxJS, which soup up this callback mechanism to make it more powerful.

RxJS comes from a library of families called ReactiveX, which is a protocol (a description of all of these operations, functions, methods). They created multiple implementations in multiple different languages: C#, RxJS, JavaScript, Scilab, Java, etc. By learning one set of operations and rules, you can port that knowledge to various other languages.

What are the main concepts behind RxJS?

  1. Observable: something you can listen to. For example, keyboard clicks, mouse events, Ajax requests coming in, WebSockets sending data over the line. Anything you can listen to, you can turn into an observable in RxJS.
  2. Operations: to manipulate your observables. You can perform an operation on observable, and get a new observable out.

To demonstrate how operations and observables work, I’ve created this pseudocode example. (see slides) There is an observable called A, which is an array of stringed numbers, the numbers “1” to “10” in string form.

We can do operations on that string, on that observable, for example, toNumber. toNumber takes the string number and turns it into an integer number. But the reactive part is that whenever observable A changes (for example, if we add the string number “2” at the end), observable B will get updated, and we’ll get the number 2 appended at the end. It reacts to changes on observable A.

We could do more operations, for example, do an Even operation, which filters the set to only include the even numbers. And again, it’s reactive: if A changes, B will get updated; if B gets updated, C will get updated. And we could do one final operation called Aggregate, which counts up the even numbers in this case.

The beauty of reactive programming is that you define D, and you know that it will get updated whenever its input has changed. You don’t have to do that manually. If we add the string number “2” at the A observable, B will get the number 2 appended at the end, then C will get the number 2 because it’s an even number, and then the aggregate will be 32.

Case study

I want to move from the pseudocode into real RxJS code by looking at a case study, the auto-complete example.

You type a query in a search box, it sends it to the backend, and the backend will provide you with suggestions. (Here we searched for “American”, and it will give you some shows that start with “American”.) The auto-complete is a surprisingly difficult thing to get completely right.

First, I want to show an imperfect example of an auto-complete, and then I want to improve it bit by bit using RxJS.


var $input = $('#search-input');

Rx.Observable.fromEvent($input, 'keyup')
  .map(e => e.target.value) // Project the text from the input
  .map(search);             // Search does an 'ajax' request
  .subscribe(function(data) {
    // Update the ui here with the data
  }, function(error) {
    // Update the ui here saying what the error was.
});

The first thing we need to do is get a reference to an input element (here, the element in which you can do your query). And then we do some RxJS magic: we create an observable for each time the input element has a “keyup” event. Whenever the user starts typing, the observable will fire, and we get events. But the events that we get are JavaScript events, which will tell you which key is active, if the Shift key was pressed, and which keycode was entered.

But we’re not really interested in that. We perform a map operation to get the value of the target, which is the actual contents of the input element that we currently have when the event was fired. And then we do another map operation, and map is exactly the same as the ES5 map operation we have on arrays. It performs a function on an input.

Then we’re going to take the query and give it to the search function, which does an Ajax request to the backend. The backend will give us, hopefully, some auto-completion results.

At the bottom we see the subscribe function, the endpoint of an observable. Whenever something changes that we are subscribed, we go into that subscribe operator, and the subscribe operator has two functions as arguments.

The first function is for when a successful thing happened. For example, when the backend provided us with auto-completion results. And we have, as a second function, the error, which gets called whenever an error occurred. For instance, if the backend was unavailable or could not provide us with results. And whenever we get a successful auto-completion, we update the UI; whenever it goes wrong, we ignore it.

Subscribe should remind you of Promises: a Promise in JavaScript can only be resolved once, while an observable can be resolved many times. Sometimes on the web, you will see that they call observables “Promises++” because they have extra functionality on top of Promises. It’s very easy in RxJS to create an observable from a Promise. There are functions for that.

But this example is imperfect. There are a couple of things that go wrong, if you put this code in production. And the first thing is this:

[2016-06-14 10:00:01] Query: “I” [2016-06-14 10:00:02] Query: “I “ [2016-06-14 10:00:03] Query: “I l” [2016-06-14 10:00:04] Query: “I lo” [2016-06-14 10:00:05] Query: “I lov” [2016-06-14 10:00:06] Query: “I love” [2016-06-14 10:00:07] Query: “I love “ [2016-06-14 10:00:08] Query: “I love G” [2016-06-14 10:00:09] Query: “I love GO” [2016-06-14 10:00:10] Query: “I love GOT” [2016-06-14 10:00:11] Query: “I love GOTO”

What you see here is the time and some queries that we sent to the backend. Each time you type in a query, in this case, “I love GOTO,” we sent all permutations of the query to the backend. We created a convenient way to spam your own server.

What we want to do is only send the query to the backend if the user has stopped typing for a while. There’s an operator for that called debounce. This effect is shown in what RxJS and the ReactiveX family of frameworks call a “marble diagram”. (see slides)

The marble diagram shows you an input stream, the operation in the middle (the bounce), and what the resulting stream would be if that operator was applied. Here the top stream has 1-6 as events. But 2-5 are close together. The resulting stream will filter out two, three, and four, and it will only keep five.

We can use this operator to limit the number of queries that we send, by simply adding this debounce operator. Then we say 500, that means if the user stops typing for half a second, then we’d send it to the backend. This way we prevent us from spamming our server.


var $input = $('#search-input');

Rx.Observable.fromEvent($input, 'keyup')
  .map(e => e.target.value) // Project the text from the input
  .debounce(500) // Wait for 500 milliseconds after the last event.
  .map(search) // Search does an 'ajax' request
  .subscribe(function(data) {
    // Update the ui here with the data
  }, function(error) {
    // Update the ui here saying what the error was.
  })

But there’s another problem. If your query has very few characters, you often get a lot of results that are not relevant for that user.

Query: “G” -> 100000 bad results Query: “Go” -> 10000 bad results Query: “Got” -> 1000 bad results Query: “Goto” -> 10 good results

Here we only get good results if our query is longer than three characters. If the query is less than that, we want to ignore those queries. And there’s an operator for that as well: filter.

filter takes a predicate function, a function which must return false based on an input. We can use that in our example by simply calling filter before we do the debounce. And we take the query and we say, “Only allow them in if the query length is bigger than three.”


var $input = $('#search-input');

Rx.Observable.fromEvent($input, 'keyup')
  .map(e => e.target.value) // Project the text from the input
  .filter(query => query.length > 3) // Three or more characters
  .debounce(500) // Wait for 500 milliseconds after the last event.
  .map(search) // Search does an 'ajax' request
  .subscribe(function(data) {
    // Update the ui here with the data
  }, function(error) {
    // Update the ui here saying what the error was.
  })

Now we’ve solved that problem, but we’re not out of the woods yet.

Sometimes we send the same query twice in a row, and this can happen if the user presses the Enter key. Say there was a five second window between the first and the second query. We probably don’t have new results within a five-second window.

What we want to do is only use the query if it’s changed from before. And there’s an operator for that as well. This one is called distinctUntilChanged.


var $input = $('#search-input');

Rx.Observable.fromEvent($input, 'keyup')
  .map(e => e.target.value) // Project the text from the input
  .filter(query => query.length > 3) // Three or more characters
  .debounce(500) // Wait for 500 milliseconds after the last event.
  .distinctUntilChanged(); // Only if the query has changed
  .map(search) // Search does an 'ajax' request
  .subscribe(function(data) {
    // Update the ui here with the data
  }, function(error) {
    // Update the ui here saying what the error was.
  })

This will only allow an event through if it’s different from the one before. And this is something we haven’t seen before: an operator which has an internal state. This operator keeps track of the previous entry that was made through it. You can have operators that have state. That’s pretty powerful.

In the resulting stream we only have alternating values. We apply this operator, distinctUntilChanged, and now we only send the query if the query has changed.

We have one big problem still left to resolve. And that problem is rather devious.

Suppose the user searches “Goto Rotterdam” but quickly realizes that the Goto is not in Rotterdam, but in Amsterdam. He changes his query to “Goto Amsterdam”. But now we have two asynchronous requests, in the air, at the same time. The results for “Goto Amsterdam” might appear before the results for “Goto Rotterdam”. In your query box you see “Goto Amsterdam”, but you see the results for “Goto Rotterdam” in your auto-complete.

Instead, we want to express the idea of only using the results of the last Ajax request that was sent to the backend. There’s an operator called flatMapLatest. If you have multiple asynchronous requests, it will only use the results of the last request that was made. That bug that we saw is no longer an issue.


var $input = $('#search-input');

Rx.Observable.fromEvent($input, 'keyup')
  .map(e => e.target.value) // Project the text from the input
  .filter(query => query.length > 3) // Three or more characters
  .debounce(500) // Wait for 500 milliseconds after the last event.
  .distinctUntilChanged() // Only if the query has changed
  .flatMapLatest(search) // Use only the Resp from the last query.
  .subscribe(function(data) {
    // Update the ui here with the data
  }, function(error) {
    // Update the ui here saying what the error was.
  })

This is the completed example. It’s relatively small, but it does a lot of work.

By using these standard operations and combining them in clever ways, you get a powerful mechanism. If I had to write this without RxJS, in pure JavaScript, my code would probably be 500 lines long, and there would be bugs in that code. By standing on the shoulders of giants, in this case RxJS, you can leverage them to accomplish great things, in relatively few lines of code.

Benefits of RxJS

The producer and the consumer are decoupled. In this case, the producer was the “keyup” event that fired the initial event, and the consumer was the subscribe. As the subscribe has its array of auto-completions, it was happy; it didn’t care how many operations we put in between there, we did not have to change the consumer or the producer, you didn’t have to change that code.

Operations are a powerful building block. You can combine them in multiple interesting ways, again, like LEGO blocks, and you can get powerful results.

Cons of RxJS

There is a steep learning curve (I like to call it a “learning cliff”). And that’s because you have to let go of your imperative model. You are used to always manipulating variables and not thinking in streams, and that it takes time before that clicks in your head. But once it does, it’s very rewarding, because you see problems that you would previously solve with imperative programming, that you can now solve with observables, in a cleaner way. And as you saw from the auto-complete example, it’s powerful.

The documentation is highly conceptual and abstract. “Returns an observable sequence that contains only distinct contiguous elements according to the keySelector and comparer.” What’s a keySelector? What’s a comparer? Contiguous elements? does anyone have a clue what this operator is, the description for this operator? This is distinctUntilChanged.

Sometimes when I’m trying to program in RxJS, and I encounter this documentation and think, “This is not the one I need.” And then later on, I find something on Stack Overflow, and it turns out I had the right one all along, but this documentation doesn’t help. The documentation shows you non-practical examples (e.g., an array of 1 to 10, and then applies the operation on that).

Recommendations

Do not use Redux or RxJS if your app is small, because you add overhead, and that might not be worth it.

For the Todo application, and for the counter application, using RxJS or Redux is overkill. But if your application starts to feel too big, you roll out this application, and all of a sudden it won’t fit in your head, you don’t know where the state should be, and new team members don’t understand where everything goes, then it might be a good time to start using Redux. It has a small learning curve, but it’s small enough you can onboard new team members relatively quickly.

If it starts feeling too big, use Redux. It’s a nice way to store your state.

When you have events that require coordination (e.g. the auto-complete example), do higher level things with asynchronous events; supercharge a component, start using RxJS. It has a very steep learning curve. Do expect some onboarding problems with newer developers, because you have to take some time to explain how it works, and how to use RxJS.

Final Words

There are many videos available about Redux and ReactiveX. The maker of Redux has his own course on Egghead.io. Also, keep an eye on dontpanic.42.nl for that series. I will start sending them out weekly.

Q & A

Q: If the state gets big, what is the performance with Redux?

Maarten: It will degrade, because you cannot keep infinite numbers of things in memory. But if you use pagination, or you only put in the store what you currently need, then you can mitigate that. It’s regular JavaScript, you can’t keep putting in memory, and of course it will lose a little bit of performance.

Q: How do you handle memory leaks in Redux?

Maarten: I don’t think that’s something you can easily do, because there’s one Redux store per application. It’s not prone to leaking, because there’s only one store, and that store will need to be removed when the user closes its tab. In that way, memory leaks are not that much of a problem in Redux, because there’s one store, and it will get cleaned up when you close your tab.

Q: Can you talk about Model-View-Controller versus the component architecture?

Maarten: I did not invent the component architecture; I was hit in the face with it. We are an Angular shop at 42, Angular 1.0, and in Angular 2.0, they went to this new architecture. But the trend has been going for a while.

React was the first one to embrace the component architecture, and Angular followed suit, and Polymer came along. Many frameworks are moving towards components. I think that if you upgrade to Angular 2, you almost don’t have a choice, you will go to the component architecture.

But the difference between components and Model-View-Controller, it’s that in a component it’s the combination of a view and a controller in one single thing. But it does not try to take them separately. Model-View-Controller, there’s often talk about having multiple controllers for the same view, and having multiple views for the same controller. And we all know that that’s secretly not true.

I’ve never written a controller for multiple views, or had the view for one single controller. The components are a way of saying, “We acknowledge the fact that they are married to each other, let’s not pretend anymore that they are two separate entities.” It’s not too much of a stretch to migrate. And in Angular 2 there are upgrade guides on how to do that.

Next Up: Realm React Native Launched!

General link arrow white

About the content

This talk was delivered live in June 2016 at goto; Amsterdam. The video was transcribed by Realm and is published here with the permission of the conference organizers.

Maarten Hus

Maarten is a developer who knows many programming languages and paradigms: from object oriented programming in Objective-C and Java, to functional programming in Clojure. Maarten has made iOS Apps written in Objective-C and HTML5 Apps written in modern frameworks such as Ember.js and Angular.js

4 design patterns for a RESTless mobile integration »

close