HTF do I move from Angular components to React components

Angular has web components.

By that I mean a modular piece of front end UI code with the following:

  • HTML template
  • TypeScript class to handle UI logic
  • Encapsulated CSS

Angular’s web components also support one way data flow via Output and Input class decorators, and dependency injection.

So how do I Reactify these concepts?

Let’s start with a basic dumb Angular Component, with a form, an input value and an output emitter, and see if we can’t shiny it up with some React:

useful-form.component.ts

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';

export interface UsefulInformation {
  firstName: string;
  crucialInformation: string;
}

@Component({
  selector: 'app-useful-form',
  templateUrl: './useful-form.component.html',
  styleUrls: ['./useful-form.component.css']
})
export class UsefulFormComponent {
  @Input() name: string;
  @Output()
  usefulUserInformation: EventEmitter<UsefulInformation> = new EventEmitter<
    UsefulInformation
  >();

  public usefulForm: FormGroup;
  public crucialOptions = ['important', 'necessary', 'crucial'];

  constructor() {
    this.usefulForm = new FormGroup({
      number: new FormControl(this.name, Validators.required),
      crucialInformation: new FormControl('', Validators.required)
    });
  }

  public submit() {
    this.usefulUserInformation.emit(this.usefulForm.value);
  }
}

useful-form.component.html

<form [formGroup]="usefulForm">
  <h2>Hello <span *ngIf="!name">person</span>{{ name }}</h2>
  <label for="number">Number</label>
  <input
    id="number"
    type="number"
    formControlName="number"
    placeholder="pick a number!"
  >
  <label for="crucialInformation">Crucial information</label>
  <select
    id="crucialInformation"
    formControlName='crucialInformation'
  >
    <option
      *ngFor="let option of crucialOptions;"
      [value]="option"
    >
      {{ option }}
    </option>
  </select>
  <button
    (click)="submit()"
    [disabled]="usefulForm.invalid"
  >Submit</button>
</form>

So here we have a component that can be embedded in other HTML, with an input passed in via a name field, that we’re rendering in the template, an output event that can be listened to, and a reactive form. There is also an *ngFor and and *ngIf.

The component can be used like below in a parent component:

<app-useful-form
  name='Rob'
  (usefulUserInformation)="handleUserInformation($event)"
></app-useful-form>

All pretty standard stuff. Let’s try and replicate this behaviour in React.

Reactified useful component

First of all I want to roughly map some Angular concepts related to components, to their React equivalents:

useful-form.js

import React from 'react';

export default class UsefulForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      number: '',
      crucialInformation: ''
    };

    this.crucialOptions = ['important', 'necessary', 'crucial'];

    this.handleNumberChange = this.handleNumberChange.bind(this);
    this.handleCrucialInformationChange = this.handleCrucialInformationChange.bind(
      this
    );
  }

  handleNumberChange(event) {
    this.setState({ ...this.state, number: event.target.value });
  }

  handleCrucialInformationChange(event) {
    this.setState({ ...this.state, crucialInformation: event.target.value });
  }

  render() {
    return (
      <form onSubmit={() => this.props.handleSubmit(this.state)}>
        <h1>Hello {this.props.name || 'person'}</h1>
        <label>
          Number:
          <input
            type="number"
            required
            placeholder="pick a number!"
            value={this.state.number}
            onChange={this.handleNumberChange}
          />
        </label>
        <label>
          Crucial information:
          <select
            required
            value={this.state.crucialInformation}
            onChange={this.handleCrucialInformationChange}
          >
            <option value=''>Pick option</option>
            {this.crucialOptions.map(option => <option value={option} key={option}>{option}</option>)}
          </select>
        </label>
        <input
          type="submit"
          value="Submit"
          disabled={!(this.state.number && this.state.crucialInformation)}
        />
      </form>
    );
  }
}

The component is used as follows in a parent component:

class App extends Component {

  handleUsefulFormSubmit(event) {
    window.alert(JSON.stringify(event));
  }

  render() {
    return (
      <UsefulForm
        name="Rob"
        handleSubmit={this.handleUsefulFormSubmit}
      />
    );
  }
}

export default App;

What are the key differences/learnings?

  1. React seems to be much less opinionated about how you do things and more flexible. It is also JavaScript, where Angular is in many ways its own thing.
  2. Forms in React seem to require more work to achieve the same as in Angular (validation, disabled buttons, updates to form values etc.). I suspect as I mess around with this I will find some nicer patterns for handling programatic driven forms.

Overall the differences are not as great as I had feared. Both allow for controlling child components from a parent component, and flowing data into and out of the component without mutating things. Also the difference between *ngIf and *ngFor and just using interpolated JavaScript is very minimal.

I am pleasantly surprised by how much I like React so far…

Leave a Reply

Your email address will not be published. Required fields are marked *