I’ve been quite excited to do some Angular to React migration and finally! Possibly the hardest frontend ticket I’ve done, mostly because Angularjs was wired. Luckily Nathan had done some great examples to get ideas from.

How the existing Angular code works

So there’s a controller, there’s an html template, and there’s a modal service that renders the modal via:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
return $q((resolve, reject) => {
this.Modal <
ApiTransaction >
{
controller: {the imported controller},
controllerAs: '$ctrl',
template: {the imported html template},
amount,
// some more arguments
}
.then((modal) => modal.closed)
.then((transaction) => {
if (transaction) {
resolve(transaction)
} else {
reject()
}
})
})

It took me some time to understand what the Modal is doing here. So it is a factory:

Factory is an angular function which is used to return the values. A value on demand is created by the factory, whenever a service or controller needs it. Once the value is created, it is reused for all services and controllers.

It is a reusable factory that creates a generic Modal that resolves to a value (typed ApiTransaction) which is the resolved value of modal.closed.

Make a “bridge” between the React component and the Angular site

  • reactular is a package made by Vend to allow you to use React component in Angular. This solves the meat of the problem.
  • Then I need a new controller that instead of taking care of the full flow, just proxies the properties from the service to the Angularified React component (which is now a template too)

Move the flow’s state management from Angular controller to a useEffectReducer hook

There are three main steps:

  • Create a transaction via an API call
  • Poll for the transaction until the transaction has reached a “finished” state e.g. Cancelled, declined, accepted, signature required
  • Reflect the transaction state to user and get follow-up actions if required e.g. validate signature

Before, it is done by a promise chain inside the controller, and the view change is done by a state owned by the controller, this.workflow.
Now to achieve similar “event-driven” state management, I use a useEffectReducer hook:

  • The whole flow starts with an initial state and an initial effect
  • When the effect finishes (promise resolves), it dispatches an event
  • The reducer handles the event and 1) exec the next effect and 2) update the state -> update the UI

Angular services

  • $q: I’ll understand it as similar to ES6 Promise for now, so that $q((resolve, reject) => { // do things then resolve() // something go run then reject})will map tonew Promise<ApiTransaction>((resolve, reject) => {// do things then resolve() // something go run then reject}). This looks like a reasonable mapping for now, will come back if it turns out not to work.
  • $timeout: a wrapper for window.setTimeout, will replace $timeout with setTimeout and replace $timeout.cancel with clearTimeout