What is reactivity?

Ember Octane has a large number of new features, but none of them are more exciting to me than automatic tracking (autotracking). Auto tracking is Ember’s new reactivity system, which allows Ember to find out when the state values ​​(for example, properties marked @tracked) have changed. This was a massive update under the hood, including a complete rewrite of some of the oldest Ember abstractions on top of the new kernel.


From a translator: Chris Garrett - works for LinkedIn and is one of the core contributors to the Ember js framework. He took an active part in creating the new edition of the framework - Ember Octane. Despite the fact that his series was written for Ember developers, it touches on concepts that are useful for all web programmers to know.

Auto tracking, at first glance, is very similar to a reactivity model Ember Classic(based on calculated properties, observers and Ember.set()). If you compare them two side by side, the main difference is where the annotations are placed. In Octane, you annotate the properties you depend on, while in Classic, you annotate getters and setters.


class OctaneGreeter {
  @tracked name = 'Liz';

  get greeting() {
    return `Hi, ${this.name}!`;
  }
}

class ClassicGreeter {
  name = 'Liz';

  @computed('name')
  get greeting() {
    return `Hi, ${this.name}!`;
  }
}

But if you dig deeper, you will find that automatic tracking is much more flexible and powerful. For example, you can automatically track the results of functions , which was not possible in Ember Classic. Take, for example, a model for a simple 2d video game:


class Tile {
  @tracked type;

  constructor(type) {
    this.type = type;
  }
}

class Character {
  @tracked x = 0;
  @tracked y = 0;
}

class WorldMap {
  @tracked character = new Character();

  tiles = [
    [new Tile('hills'), new Tile('beach'), new Tile('ocean')],
    [new Tile('hills'), new Tile('beach'), new Tile('ocean')]
    [new Tile('beach'), new Tile('ocean'), new Tile('reef')],
  ];

  getType(x, y) {
    return this.tiles[x][y].type;
  }

  get isSwimming() {
    let { x, y } = this.character;

    return this.getType(x, y) === 'ocean';
  }
}

, isSwimming , character ( x / y). , , getType, . CP (. .: Computed Properties — Ember Classics), - . .


, Ember Classic. ! , , Ember , Octane.


-, , . , . , . , , , , .


7 . , « », , :


  1. ? ←
  2. ?
  3. — TrackedMap
  4. — @localCopy
  5. — RemoteData
  6. — effect()

, , Octane. -, , , ! Discord.


: «»?



, « », , . . , .


: .

«» , « » « ». , « Ember» « Vue», , . , .


, ! .

( (streams))


, , , , : «» (declarative) «» (state). « » , «» «» ? "" (state)? , , .


(state)


«» «». , , ; , "". JavaScript :


  • , let const
  • , Map Set

, (root state), , . , , , . :


let a = 1;
let b = 2;

function aPlusB() {
  return a + b;
}

aPlusB() , a b. , . , , a b, aPlusB(). , a b , aPlusB() , .



, «», «» «», , , . , :


, , , .
, - .

«» , (). , — « »? , , , , , , , ( , ).


aPlusB. , , :


  1. ( ).
  2. a + b.

let a = 1;
let b = 2;

let aPlusB = a + b;

, , . , a b. aPlusB , :


let a = 1;
let b = 2;

let aPlusB = a + b;

//  `a`
a = 4;
aPlusB = a + b;

//  `b`
b = 7;
aPlusB = a + b;

, . , a, aPlusB. API , , :


export default class Counter extends Component {
 count = 0;

  @action
    incrementCount() {
    this.count++;
    this.rerender();
  }
}

, rerender() , , . , , - , , .


, «» . aPlusB().


let a = 1;
let b = 2;

function aPlusB() {
  return a + b;
}

, , , . , «» , . , aPlusB() , . , , , .


, , Ember, Vue Svelte, (, ) JSX , , JSX . HTML — , , , . HTML, , . «» , , , DOM, , DOM , .


FP (Functional Programming)?


. «» «» «». , . , HTML, , - , .


( FP), . «» — .


let a = 1;
let b = 2;

function plus(first, second) {
  return first + second;
}

//  a + b;
plus(a, b);

. , , .



, ( ). , FP , , , . , , , .


, . , , , .


, , . , «» FP, - (, , . « », !). , .



, :


  1. «» « ». , , , .
  2. «» , . ( , ):
    • , , , .
    • — , , , .
  3. « » , , , . HTML , — , . , , .

. , , , - , - . , , «» .


: , . , , )


All Articles