Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should I use Redux in Angular as far as Angular is two-way data binding?

As far as I understand Redux is mainly intended to enable the two-way data binding in a javascript app. This is very usefull for frameworks that are not two-way data binding, as React for instance. But why using it in Angular that is already natively two-way data binding ?

To illustrate my question here is the code that I use in native Angular to make a store that enables the communication of a variable state between two Angular components :

1) The store

import { Injectable } from '@angular/core';
@Injectable({
  providedIn: 'root'
})
export class StoreService {
  customer: any;
  constructor() { }
}

This store is a native Angular service where I only declare the variable customer (yes typing would be better but i want to shorten it as much as possible).

2) The asynchronous api service

import { Injectable } from '@angular/core';
import { StoreService } from './store.service'

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  constructor( private store: StoreService ) { }

  getData() {
    setTimeout(()=> {
      this.store.customer = {
        name: 'Bob',
        age: 25
      }
    }, 2000);
  }
}

This api service has only one method getdata() that retrieves the customer data asynchronously. I could use an http.get method and in this case the code inside the setTimeout would be the code in the next() function of the observable subscribe. Note that I instanciate the return of the async process directly in the former store.

3) A component using the store

import { Component, OnInit } from '@angular/core';
import { ApiService } from '../api.service'
import { StoreService } from '../store.service'

@Component({
  selector: 'app-mycomponent',
  templateUrl: './mycomponent.component.html',
  styleUrls: ['./mycomponent.component.css']
})
export class MycomponentComponent implements OnInit {

  constructor(
    private api: ApiService,
    private store: StoreService
  ) { }

  ngOnInit() {

    this.api.getData();

  }
}

Note that except importing the services store and api, I use only one line of code, the one that call for data. This line would be useless if an other component, or any other service, would have already populated the store (see the second component below)

4) The HTML template of the component

<ul>
  <li>Name : {{store.customer?.name}}</li>
  <li>Age : {{store.customer?.age}}</li>
</ul>

Note that I use directly the store in the template to ensure the two-way data binding with the other components that also imports the same store. Note the use of the elvis operator ?. that manages the async variable store.customer.

5) An other component, that modifies the store

import { Component, OnInit } from '@angular/core';
import { StoreService } from '../store.service'

@Component({
  selector: 'app-myothercomponent',
  templateUrl: './myothercomponent.component.html',
  styleUrls: ['./myothercomponent.component.css']
})
export class MyothercomponentComponent implements OnInit {   
  constructor(private store: StoreService) { }
}

I only import the store, I have no need for any other line of code.

6) The HTML template of the former component

<p>
  <input type="text" [(ngModel)]="store.customer && store.customer.name">
</p>

Note the special way to handle the asynchronism because of the use of ngModel. By the way think to import the FormsModule in dorder to handle the input in the HTML.

Now change the value of the customer name in the second component and you will see its direct and instantaneous change in the first component.

This two-way data binding of Angular is so powerfull, and so simple to setup and use, that I really wonder why I should rather use Redux, or NgRx. Could you please explain me why I should ?

Thanks for help.

like image 1000
Hervé Le Cornec Avatar asked Dec 14 '22 09:12

Hervé Le Cornec


1 Answers

As far as I understand Redux is mainly intended to enable the two-way data binding in a javascript app. This is very usefull for frameworks that are not two-way data binding, as React for instance. But why using it in Angular that is already natively two-way data binding?

That statement is wrong, and so everything else after it is based upon an incorrect impression of what problem Redux solves.

It has nothing to do with data binding, but you're close to the problem it solves.

Redux solves the problem that let x = {y: 2}; x.y = 3; hides a mutation of a value, and everything else in your JavaScript application will have a difficult time knowing when x.y changes.

Historically, frameworks like AngularJS 1.x used watchers that would compare a template expression with its previous value to see if it has changed, but if you're template is referencing x and the inner value x.y has changed, then the watcher doesn't see it.

React has only one-way data bindings, but is suffers from the same side effect. If you pass x as a property to <MyComponent x={x}/> the component will not render an update if x.y changes.

Angular suffers from this problem as well.

Programmers solve this problem by cloning objects so that the data bindings would see the mutation as changing, and render the changes to the DOM.

For example;

let x = {y:1};
let next = {...x, y:2};
console.log(x === next); // prints false

Instead of directly mutating x.y = 2 the above code creates a new object with the new value, and the comparison between x and the next value yields false which tells most frameworks like Angular or React that the data has changed, and anything that needs it should be updated.

So what does this have to do with Redux?

Redux is a reducer framework. It's based upon the idea that it uses reducer functions to perform the above work. The mutation of {...x, y:2} is really simple, but Redux provides tools for the developer to do that in easier to manage ways. For example, selectors and actions are ways of abstracting the work that produces a new mutated value.

If JavaScript could see that x !== x when x.y is changed, then we wouldn't need Redux.

like image 160
Reactgular Avatar answered May 23 '23 03:05

Reactgular