Giving react a try after a decade working with Angular

Jorge Vergara pointing at a target showing an arrow in the bullseye

After a little over a decade of working with Angular (ever since the days of Angular 1, now AngularJS) I decided giving react a serious chance, mostly due to what I’ve seen in my current job search, most of the job posts I see for front end developer have some sort of requirement for react.

Yeah, even the “Angular Developer” roles have the line that says “3-4 years with react.js is preferred” 😅

Here is a collection of things I’ve learned about react APIs and how they compare to their Angular alternatives.

NOTE: This is based on what I am learning at the moment, there’s a chance that I am wrong in some parts, since I still don’t know what I don’t know yet. If that’s the case and you spot something I can correct, do let me know, you can find me on the Bsky app

useContext

This seems to me like the alternative to Angular services.

What happens when you want to use some properties in different components? You can pass the properties to each child component to pass them down as many times needed to get to the component you are going to use them, this in both frameworks, since it makes little sense to pass properties to components that aren’t going to need them, Angular came with services (if I remember correctly we called them Providers way back when). This is what useContext() achieves in react.

For example, let’s say we have a ParentComponent that renders other component (that renders other component, and so on), and you have a userProfile in the main component that one to use in another component down the line.

Without context, you’d pass the property along the components to have it available, like this:

interface UserProfile {
  name: string;
  email: string;
  profilePictureUrl: string;
}

function ParentComponent() {
  const userProfile: UserProfile = {
    name: 'Jorge',
    email: 'jorge@jorgevergara.co',
    profilePictureUrl: '/profiles/jorge.png',
  };

  return (
    <>
      <h1>{`Hello ${userProfile.name}!`}</h1>
      <ChildComponent userProfile={userProfile} />
    </>
  );
}

function ChildComponent(userProfile: UserProfile) {
  return (
    <>
      <h1>Child Component</h1>
      <GrandChildComponent userProfile={userProfile} />
    </>
  );
}

function GrandChildComponent(userProfile: UserProfile) {
  return (
    <>
      <h1>GrandChildComponent</h1>
      <h2>{`Welcome into another component ${userProfile.name}`}</h2>
    </>
  );
}

Now, there’s nothing technically wrong with this, but it means that every time something changes or it’s added, you’ll have to keep track of all the components that are passing that property (and not even using it).

That’s where context comes into play, you can use createContext and useContext to create a context of the property, and have it available in any child component by wrapping the parent component in a context provider.

That sounded like gibberish the first time I read it, so let’s walk through the code:

// We import both createContext and useContext
import { createContext, useContext } from 'react';

// We create a context for the user profile
const UserProfileContext = createContext();

interface UserProfile {
  name: string;
  email: string;
  profilePictureUrl: string;
}

function ParentComponent() {
  const userProfile: UserProfile = {
    name: 'Jorge',
    email: 'jorge@jorgevergara.co',
    profilePictureUrl: '/profiles/jorge.png',
  };

  // We wrap the ParentComponent in the context provider
  // and pass the userProfile as its value. Now we don't
  // need to pass it in the components.

  return (
    <UserContext.Provider value={user}>
      <h1>{`Hello ${userProfile.name}!`}</h1>
      <ChildComponent />
    </UserContext.Provider>
  );
}

function ChildComponent() {
  return (
    <>
      <h1>Child Component</h1>
      <GrandChildComponent />
    </>
  );
}

function GrandChildComponent() {
  // Here we use that context and it's
  // now available in our component.

  const userProfile = useContext(UserProfileContext);

  return (
    <>
      <h1>GrandChildComponent</h1>
      <h2>{`Welcome into another component ${userProfile.name}`}</h2>
    </>
  );
}

I’m sure there are other ways to accomplish this, so I’ll probably come back to this post to update it in the future 🙌🏽

useState()

This is my favorite hook so far 😉, after spending a few years working with NgRx and ComponentStore, its simplicity felt like a breath of fresh air.

It allows you simple state handling to keep your components reactive.

For example, I want to have a single source of truth for my userProfile value, in Angular, since this is a simple task, I’d immediately reach for creating a BehaviorSubject.

I’d create the subject and then everywhere I’d need to update it I’d do something like this.userProfileSubject.next(newValue), and every update would reactively update all the places where my profile is being used.

In react I can achieve the same with the useState() hook. You’d instead declare the userProfile with this hook like this:

const [userProfile, setUserProfile] = useState({
  name: 'Guest',
  email: '',
  profilePictureUrl: '',
});

The useState() hook returns the state and the function to update the state, it also takes a default value.

Then you can use your state (in our case the userProfile) normally like:

import { useState } from 'react';

function UserProfilePage() {
  const [userProfile, setUserProfile] = useState({
    name: 'Guest',
    email: '',
    profilePictureUrl: '',
  });

  function updateProfilePicture(profilePicture) {
    const profilePictureUrl = fancyUploaderThatReturnsThePictureUrl(profilePicture);

    // Here you have access to the previous value of the state in case you need it
    setUserProfile(previousUserProfile => {
      // Then do whatever you want and return the new state or new userProfile.
      return {
        ...previousUserProfile,
        profilePictureUrl,
      };
    });
  }

  return (
    <div>
      <img src={userProfile.profilePictureUrl} />
      <br />

      <input type="file" onChange={event => updateProfilePicture(event)} />
    </div>
  );
}

useReducer

When your state management has a bit more complexity.

When comparing with Angular, if useState() for me would be like using a BehaviorSubject, then useReducer would be like reaching for ngrx component store.

The main idea is that a reducer takes in a state, and an action.

Then, you dispatch different actions, and in your reducer you’re free to do whatever you want depending on the action you dispatched.

I’m going to take an example directly from the react docs and explain it the comments:

import { useReducer } from 'react';

// The reducer takes in the current state
// and the action you want to dispatch
function reducer(state, action) {
  // Then you can decide on what to do
  // depending on the action you dispatched
  switch (action.type) {
    case 'incremented_age':
      // And you return the new version of the state
      return {
        age: state.age + 1,
      };

    case 'reduced_age':
      return {
        age: state.age - 1,
      };
  }
}

export default function Counter() {
  // Here the useReducer returns the dispatch method and the state
  // You initialize it with your reducer function, and the initial state
  const [state, dispatch] = useReducer(reducer, { age: 42 });

  // Then you dispatch different actions from inside your component.
  return (
    <>
      <button onClick={() => dispatch({ type: 'incremented_age' })}>Increment age</button>

      <p>Hello! You are {state.age}.</p>

      <button onClick={() => dispatch({ type: 'reduced_age' })}>Reduce age</button>
    </>
  );
}

createPortal()

This was a nice find, it lets you work on your component and render an element somewhere else in the DOM, lets say for example you plan on triggering an alert from your component, but to style it better the alert needs to be part of the <body>, not whatever component you’re using.

In Angular you can to use the CDK portal for that, in react, you have a method called createPortal():

import { useState } from 'react';
import { createPortal } from 'react-dom';

export function App(props) {
  const [showPicture, setShowPicture] = useState(false);

  return (
    <div className="App">
      <button onClick={() => setShowPicture(prevState => !prevState)}>
        {!showPicture ? 'Show' : 'Hide'} Picture
      </button>
      {showPicture
        ? createPortal(<ImagePopOver src={'source_to_my_image'} />, document.body)
        : null}
    </div>
  );
}

The simplest explanation is that the createPortal takes an element, and puts it in whatever DOM node you say, in this case, we’re putting the popover directly on the body.

Error Boundaries

In Angular, we can use different templates if we have loading or error (or whatever other) states, for example, for an error template you can do:

<ng-template [ngIf]="serverLoaded" [ngIfElse]="error">
  <div>My proper template</div>
</ng-template>

<ng-template #error>
  <div>My error template</div>
</ng-template>

In react I found the ErrorBoundary tag that lets me achieve the same, for example, I can wrap my component in an error boundary like this:

import { ErrorBoundary } from 'react-error-boundary';

export function App() {
  return (
    <ErrorBoundary fallback={<p>Something went wrong</p>}>
      <MySuperAwesomeUI />
    </ErrorBoundary>
  );
}

function MySuperAwesomeUI() {
  return <div>Super Awesome UI we have here</div>;
}

If something goes wrong, it’ll load the text saying so. We can also pass custom components to that boundary, like:

import { ErrorBoundary } from 'react-error-boundary';

export function App() {
  return (
    <ErrorBoundary fallback={<MySomethingWentWrongUI />}>
      <MySuperAwesomeUI />
    </ErrorBoundary>
  );
}

function MySuperAwesomeUI() {
  return <div>Super Awesome UI we have here</div>;
}

function MySomethingWentWrongUI() {
  return <div>A full page showing what went wrong</div>;
}

And that’s just to name a few, there are lots of other hooks, for example useEffect() to handle side-effects, like calling an asynchronous API and updating something inside of it.

Or useOptimistic() that lets you optimistically update the UI 🔥

So far I’m having a lot of fun learning, the one thing I don’t like is that since I come from frameworks like Django and Angular, I am used to having a lot of decisions already made for me, react seems more like, “Here is the set of APIs, use them however you see fit”

But hoping to get back to this post and edit it telling you I changed my mind and that I am happily using either nextjs, the Tan Stack, One, or whatever react framework that’s the flavor of the week 🙌🏽