Credits: Sitepoint

Two React Fans sent to Angular Battleground

Feelings

Tips

How to choose between the two

Let's get started!

Two different basic stack

Modern Javascript Base

Angular

Two different learning curves

  • Small basis to learn
  • Lots of others library
  • Lots of design patterns
  • Observables (RxJS)
  • Then you are all set

Launch your project

Prepare your coding gear

React Dev Tool

Augury

Prepare your coding gear

Documentation

Let's Code!

Writing components

// Hello.jsx

export class Hello extends Component {
  render() {
    return (
      <div>
        Hello { this.props.name }!
      </div>
    );
  }
}

Hello.propTypes = {
  name: React.PropsTypes.string
}

Hello.defaultProps = {
  name: 'ParisJS'
};
//app.module.ts

@NgModule({
  declarations: [
    HelloComponent,
  ],
})
export class AppModule {}
//hello.component.html

<div>
    Hello {{ name }}!
</div>
//hello.component.ts

@Component({
  selector: 'hello-component',
  templateUrl: './hello.component.html',
  styleUrls: ['./hello.component.less'],
})
export class HelloComponent {
  @Input() name = 'ParisJS';
}

Writing component - JSX

Writing component -JSX

<div style="list">
  <div style="list-elem">1</div>
  <div style="list-elem">2</div>
</div>

// instead of
<ul style="list">
  <li style="list-elem">1</li>
  <li style="list-elem">2</li>
</ul>

<div onClick="function">Button</div>
// instead of
<button onClick="function">Button</button>


pas clair

Accessibility

Usual user behaviours

Communication between components

Props

Callback

@Input

@Output and EventEmitter

// declare fields
@Input() data: string;
@Output() handleEvent = new EventEmitter();
// use it
handleEvent.emit(value)
<MyChildComponent 
    [data]="data" 
    (handleEvent)="myFunction($event)">
<MyChildComponent 
    data={data} 
    handleEvent={myFunction}>
// declare in PropTypes
data: PropTypes.string,
handleEvent: PropTypes.func,
// use them
this.props.handleEvent(value)

App Data Management

  • Context API
  • State managers
  • Data services (singletons)
  • State managers

The first obstacles!

Writing components - wrappers

<TodoList title={'Todo list'}>
  {this.props.listItems.map((listItem) =>
    (<ListItem item={listItem} />)
  )}
</TodoList>
<todo-list [title]="Todo list">
  <todo-list-item 
    *ngFor="let listItem of listItems" 
    [item]="listItem" />
</todo-list>
<todo-list>
  <h1>Todo list</h1>
  <ul class="group">
    <todo-list-item>
      <li class="item">learn react</li>
    </todo-list-item>
    <todo-list-item>
      <li class="item">learn angular</li>
    </todo-list-item>
    <todo-list-item>
      <li class="item">decide</li>
    </todo-list-item>
  </ul>
</todo-list>

Harder to style and understand HTML

<h1>Todo list</h1>
<ul class="group">
  <li class="item">learn react</li>
  <li class="item">learn angular</li>
  <li class="item">decide</li>
</ul>

Component Lifecycle

  • constructor
  • ngOnChanges
  • ngOnInit
  • ngOnDestroy
  • 5 more methods
  • constructor
  • getDerivedStateFromProps
  • componentDidMount
  • componentWillUnmount
  • render
  • 4 other methods

Sometimes displaying similar APIs...

...sometimes very different ones...

Component Lifecycle - Virtual DOM

  • Compares JS objects
  • Only renders changed elements

Overall both frameworks are performant, React more for massive amount of data

Component lifecycle

Do not calculate methods in template in Angular

... which can end up in little suprises

import { Component } from '@angular/core';

@Component({
  selector: 'app-header',
  templateUrl: `
    <div class="header-container">
      {{ countMyRenders() }}
    </div>
  `,
})
export class HeaderComponent {
  countMyRenders() {
    console.count('render of header component');
  }
}

Can render erratically - even if inputs don't change

Observables are powerful...

  • Between a promise and an event
  • Reactive programing - your UI listens and reacts to changes
  • Powerful and particularly adapted to Real Time

"You can think of an observable as an array whose items arrive asynchronously over time." Angular docs

...but complicated to master...

@Injectable()
export class ApiService {
  constructor(private toolService: ToolService) {}

  getAPIData(): Observable<ApiAnswer> {
    return this.http.get('API_URL');
  }
}

...

// in a component
this.ApiService.getAPIData(); // does not trigger the call
this.ApiService.getAPIData().subscribe(); // does trigger the call

It's a new paradigm to learn alltogether

  • ~ 20 different classes (observable, subject ...)
  • ~ 120 operators =>

...and you don't have a choice

DOM

LISTENERS

Dependency injection can make your life easier
... but your Hands and Eyes Sore

Makes testing easier

Makes code Flexible/Easier to re-use

@Injectable()
export class ApiService {
  method() {...}
}

...

@NgModule({
  declarations: [],
  imports: [],
  providers: [
    ApiService,
  ],
})
export class AppModule {}

...

@Component({
  selector: 'app-header',
})
export class HeaderComponent {
  constructor (apiService: ApiService) {
    this.apiService.method();
  }
}

Adds a lot of boiler-plate code in tests

Tests - Angular

describe('MyComponent', () => {
  let fixture;
  let component;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        FormsModule,
        HttpClientModule,
      ],
      declarations: [
        MyComponent,
      ],
      schemas: [NO_ERRORS_SCHEMA]
    }).compileComponents();
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.debugElement.componentInstance;
    fixture.detectChanges();
  }));
  ...

Boiler Plate

Mock with DI

Complicated

API

Tests - Angular

  ...

  it('should be a great test', fakeAsync(() => {
    // mandatory to refresh dom
    fixture.detectChanges();
    // mandatory to wait for asynchrone events to finish
    tick()
    fixture.detectChanges();
    
    ...
    fixture.detectChanges();
    tick()
    fixture.detectChanges();
  }));
  ...

Component lifecycle to update manually

Tests - Angular

ERROR ZoneAwareError {__zone_symbol__error: Error: Uncaught (in promise): Error
Error
at Error.ZoneAwareError (http://localhost:4200/vendor.……}message: (...)name: (...)originalStack: (...)promise: ZoneAwarePromiserejection: Errorstack: (...)task: ZoneTasktoSource: function ()toString: function ()zone: ZonezoneAwareStack: (...)__zone_symbol__error: Error: Uncaught (in promise): 
Error
Error
at Error.ZoneAwareError (http://localhost:4200/vendor.bundle.js:97291:33)
at ZoneAwareError (http://localhost:4200/vendor.bundle.js:97288:35)
at injectionError (http://localhost:4200/vendor.bundle.js:2061:86)
at noProviderError (http://localhost:4200/vendor.bundle.js:2099:12)
at ReflectiveInjector_._throwOrNull (http://localhost:4200/vendor.bundle.js:3601:19)
at ReflectiveInjector_._getByKeyDefault (http://localhost:4200/vendor.bundle.js:3640:25)
at ReflectiveInjector_._getByKey (http://localhost:4200/vendor.bundle.js:3572:25)
at ReflectiveInjector_.get (http://localhost:4200/vendor.bundle.js:3441:21)
at AppModuleInjector.NgModuleInjector.get (http://localhost:4200/vendor.bundle.js:4406:52)
at resolveDep (http://localhost:4200/vendor.bundle.js:11810:45)
at createClass (http://localhost:4200/vendor.bundle.js:11673:147)
at createDirectiveInstance (http://localhost:4200/vendor.bundle.js:11504:37)
at createViewNodes (http://localhost:4200/vendor.bundle.js:12853:49)
at createRootView (http://localhost:4200/vendor.bundle.js:12758:5)
at callWithDebugContext (http://localhost:4200/vendor.bundle.js:13889:42)
at Error.ZoneAwareError (http://localhost:4200/vendor.bundle.js:97291:33)
at ZoneAwareError (http://localhost:4200/vendor.bundle.js:97288:35)
at injectionError (http://localhost:4200/vendor.bundle.js:2061:86)
at noProviderError (http://localhost:4200/vendor.bundle.js:2099:12)
at ReflectiveInjector_._throwOrNull (http://localhost:4200/vendor.bundle.js:3601:19)
at ReflectiveInjector_._getByKeyDefault (http://localhost:4200/vendor.bundle.js:3640:25)
at ReflectiveInjector_._getByKey (http://localhost:4200/vendor.bundle.js:3572:25)
at ReflectiveInjector_.get (http://localhost:4200/vendor.bundle.js:3441:21)
at AppModuleInjector.NgModuleInjector.get (http://localhost:4200/vendor.bundle.js:4406:52)
at resolveDep (http://localhost:4200/vendor.bundle.js:11810:45)
at createClass (http://localhost:4200/vendor.bundle.js:11673:147)
at createDirectiveInstance (http://localhost:4200/vendor.bundle.js:11504:37)
at createViewNodes (http://localhost:4200/vendor.bundle.js:12853:49)
at createRootView (http://localhost:4200/vendor.bundle.js:12758:5)
at callWithDebugContext (http://localhost:4200/vendor.bundle.js:13889:42)
at resolvePromise (http://localhost:4200/vendor.bundle.js:96964:31) [angular]
at resolvePromise (http://localhost:4200/vendor.bundle.js:96935:17) [angular]
at http://localhost:4200/vendor.bundle.js:97012:17 [angular]
at Object.onInvokeTask (http://localhost:4200/vendor.bundle.js:4965:37) [angular]
at ZoneDelegate.invokeTask (http://localhost:4200/vendor.bundle.js:96665:36) [angular]
at Zone.runTask (http://localhost:4200/vendor.bundle.js:96465:47) [<root> => angular]
at drainMicroTaskQueue (http://localhost:4200/vendor.bundle.js:96845:35) 
[<root>]
at HTMLAnchorElement.ZoneTask.invoke 
(http://localhost:4200/vendor.bundle.js:96723:25) [<root>]get message: function ()set message: function (value)get name: function ()set name: function (value)get originalStack: function ()set originalStack: function (value)get stack: function ()set stack: function (value)get zoneAwareStack: function ()set 
zoneAwareStack: function (value)__proto__: Object
defaultErrorLogger @ core.es5.js:1085
ErrorHandler.handleError @ core.es5.js:1145
next @ core.es5.js:4774
schedulerFn @ core.es5.js:3848
SafeSubscriber.__tryOrUnsub @ Subscriber.js:234
SafeSubscriber.next @ Subscriber.js:183
Subscriber._next @ Subscriber.js:125
Subscriber.next @ Subscriber.js:89
Subject.next @ Subject.js:55
EventEmitter.emit @ core.es5.js:3834
NgZone.triggerError @ core.es5.js:4205
onHandleError @ core.es5.js:4166
ZoneDelegate.handleError @ zone.js:338
Zone.runGuarded @ zone.js:142
_loop_1 @ zone.js:557
drainMicroTaskQueue @ zone.js:566
ZoneTask.invoke @ zone.js:424

Unclear error messages

Tests - React

+ Enzyme

Snapshot testing

No need for a browser

Easy to manipulate props and state

Hard to mock business functions

describe('Super test case', () => {
  beforeEach(() => {
    wrapper = shallow(<MyComponent {...props} />);
  });

  it('should be a great test', () => {
    // Set state
    wrapper.setState({ name: 'bar' })
    // Call component Class methods
    wrapper.instance().handleClick();
    // check children components props
    expect(image.prop('src')).toBe('url');
    // Simulate events
    wrapper.find(MyOtherComponent).simulate('click');
  });
});

Simple API

Tests - React vs Angular

Architecture

Angular Modularity

Architecture - Angular

Core module

Heroes module

Shared module

App module

On one side the doc offers a solution for everything...

source: https://angular.io/guide/styleguide#overall-structural-guidelines

A complete style guide

One proposed organisation

One way of naming files

Architecture - React

... on the other side everything is up to you

.js vs .jsx

File naming

Business logic files

Organizing by Page

It is extremely rare to find two React projects with the same architecture

Styling

Typing

Function vs Class

shared components

Difference of philosophy

Angular is a Framework

React is a library

Difference of philosophy

Both can make to a good recipe...

...or end up in spaghetti-code

How to choose ?

How to choose - Team Background

Typed / Object Oriented

How to choose - Team Background

How to choose - Team Size

How to choose - Project

How to choose - Targeting Mobile

How to choose - Real Time

How to choose - Personal Preference

You prefer to choose for yourself / tailor your stack to your needs

You prefer everything to be decided for you

How to choose - Personal Preference

Our Feeling

  is more structured but not as fun as 

 

 

Our Tips

 

Working with         helped us with component architecture and state management

 

Working with          helped us to structure our projects and stay closer to HTML

How to choose

How is the Team ?

 

How is the Project  ?

Thank you

React vs Angular ParisJS

By Loïc Carbonne

React vs Angular ParisJS

  • 2,010