-
Notifications
You must be signed in to change notification settings - Fork 30
/
06s-reactivity.md.erb
114 lines (82 loc) · 7.1 KB
/
06s-reactivity.md.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
---
title: Reactividad
slug: reactivity
date: 0006/01/02
number: 6.5
points: 5
sidebar: true
photoUrl: http://www.flickr.com/photos/ikewinski/9632550278/
photoAuthor: Mike Lewinski
contents: "Aprenderemos cómo funciona el sistema reactivo de dependencias.|Entenderemos su motivación y cómo nos permite escribir código declarativo.|Aprenderemos a utilizar código avanzado que hace uso de datos reactivos."
paragraphs: 20
---
Si las colecciones son la característica principal de Meteor, podemos decir que la **reactividad** es lo que hace útil esta característica.
Las colecciones trasforman radicalmente la forma en que la aplicación maneja cambios en los datos. En lugar de tener que comprobarlo manualmente (por ejemplo, con una llamada AJAX) y actualizar los cambios en el código HTML, con Meteor, los cambios pueden llegar en cualquier momento y aplicarse a la interfaz de usuario sin más complicaciones.
Vamos a verlo con detenimiento: entre bastidores, Meteor es capaz de cambiar *cualquier* parte de la interfaz de usuario cuando se actualiza una colección subyacente.
La forma *imperativa* (o "a mano") de hacerlo sería utilizar `.observe()`, una función del cursor que dispara callbacks cuando los documentos seleccionados cambian. De esta forma, podríamos hacer cambios en el DOM (el HTML de nuestra página web) a través de esos callbacks. El código resultante sería algo como esto:
~~~js
Posts.find().observe({
added: function(post) {
// when 'added' callback fires, add HTML element
$('ul').append('<li id="' + post._id + '">' + post.title + '</li>');
},
changed: function(post) {
// when 'changed' callback fires, modify HTML element's text
$('ul li#' + post._id).text(post.title);
},
removed: function(post) {
// when 'removed' callback fires, remove HTML element
$('ul li#' + post._id).remove();
}
});
~~~
Es probable que te des cuentas de que este tipo de código va a tender a hacerse muy complejo muy rápidamente. Imagínate lo que sería tratar con cambios en *cada uno de los atributos* del post, y tener que cambiar HTML complejo dentro de `<li>`. Por no hablar de casos más extremos cuando empecemos a depender de múltiples fuentes de información que pueden cambiar en tiempo real.
<% note do %>
### ¿Cuándo *deberíamos* usar `observe()`?
A veces es necesario usar el patrón anterior, sobre todo cuando se trabaja con widgets de terceros. Por ejemplo, imaginemos que queremos añadir o quitar puntos en un mapa en tiempo real dentro de una Colección (por ejemplo, para mostrar las localizaciones de los usuarios actualmente conectados).
En tal caso, tendrás que usar callbacks `observe()` para conseguir que el mapa "hable" con la colección Meteor y saber cómo reaccionar a los cambios en los datos. Por ejemplo, tendrías que confiar en los callbacks `added` y `removed` para llamar a los métodos `dropPin` o `removePin()` de la API del mapa.
<% end %>
### Un enfoque declarativo
Meteor nos proporciona una forma fácil de hacer todo esto: la reactividad, que, en esencia es un enfoque **declarativo**. De esta forma, podemos definir la relación entre los objetos una sola vez y podemos estar seguros de que se mantendrán siempre sincronizados, en vez de tener que especificar el comportamiento de cada uno de los posibles cambios.
Este es un concepto realmente poderoso, ya que, en un sistema de tiempo real puede haber muchas entradas y todas pueden cambiar de forma impredecible. Con "declarativo", queremos decir que, cuando se renderiza HTML basado en una o varias fuentes de datos reactivos, Meteor se hace cargo de la tarea de sincronizar las fuentes y, de forma trasparente, asumir el trabajo sucio de mantener la interfaz de usuario actualizada.
Y todo esto, sólo para acabar diciendo que, en lugar de tener que pensar en callbacks tipo `observe`, Meteor nos permite escribir:
~~~html
<template name="postsList">
<ul>
{{#each posts}}
<li>{{title}}</li>
{{/each}}
</ul>
</template>
~~~
Y obtener la lista de posts con:
~~~js
Template.postsList.helpers({
posts: function() {
return Posts.find();
}
});
~~~
Cuando cambian los datos reactivos, es Meteor el que, entre bastidores, está creando callbacks `observe()`, y redibujando las secciones pertinentes del HTML.
### Seguimiento de dependencias en Meteor: Computaciones
Meteor es un framework reactivo y en tiempo real, pero no *todo* el código incluido en una aplicación Meteor es reactivo. Si así fuera, la aplicación entera se volvería a ejecutar cada vez que cambia algo. En vez de eso, la reactividad se limita a áreas específicas, que llamamos **computaciones**.
En otras palabras, una computación es un bloque de código que se ejecuta cada vez que cambia una de las fuentes de datos reactivos de las que depende. Si tienes una fuente reactiva (por ejemplo, una variable de sesión) y quieres responder reactivamente a ella, tendrás que crear una computación.
Ten en cuenta que, por lo general, no es necesario hacerlo de forma explícita, ya que, Meteor provee de una computación especial a cada una de nuestras plantillas (lo que significa que puedes estar seguro de que tus plantillas reflejarán los datos de los que dependen).
Todas las fuentes de datos reactivas hacen un seguimiento de todas las computaciones que las usan para poder avisarles de que su valor ha cambiado y deben "volver a computarse". Para hacer esto, se llama a la función `invalidate()`.
Generalmente, las computaciones se establecen para volver a evaluar su contenido, y esto es lo que ocurre con las computaciones que Meteor crea para las plantillas (aunque, además, se añade un poco más de magia para redibujar la página de manera más eficiente). Aunque, si es necesario, se puede tener más control sobre lo que hace una de estas computaciones, en la práctica, no va a ser necesario porque Meteor nos va a dar justo el comportamiento que vamos a necesitar.
### Configuración de una computación
Ahora que entendemos la teoría de las computaciones, configurar una te va a parecer desproporcionadamente fácil. Usaremos la función `Tracker.autorun` para encerrar un bloque de código en una computación y hacerlo reactivo:
~~~js
Meteor.startup(function() {
Tracker.autorun(function() {
console.log('There are ' + Posts.find().count() + ' posts');
});
});
~~~
Ten en cuenta que tenemos que envolver el bloque `Tracker` dentro de un bloque `Meteor.startup()` para asegurarnos que solo se ejecute una vez cuando Meteor ha terminado de cargar la colección `Posts`.
Entre bastidores, `autorun` crea una computación, y la configura para que se vuelva a evaluar cada vez que cambian las fuentes de datos de las que depende. El ejemplo es muy sencillo, simplemente muestra el número actual de posts en la consola. Como `Posts.find()` es una fuente de datos de reactiva, será esta la que se encargue de hacer que se vuelva a ejecutar la computación cada vez que cambie el número de posts.
~~~js
> Posts.insert({title: 'New Post'});
There are 4 posts.
~~~
El resultado es que, de forma fácil y natural, podemos escribir código que usa datos reactivos, sabiendo que, por detrás, el sistema de dependencias se encargará de todo en todo momento.