What's New in StateAdapt 2.0.0
Tags: webdev, typescript, angular, news
Published: 2023-11-05 13:00:00

[Three weeks ago](https://github.com/state-adapt/state-adapt/releases/tag/v2.0.0), [StateAdapt](https://github.com/state-adapt/state-adapt) released a new major version.

The main changes here is a rework of the `adapt` API that might have been confusing to some, especially newcomers.

Today we will have a brief overview of those breaking changes.

> If you are more interested in watching than reading, take a look at [Mike Pearson's video](https://www.youtube.com/watch?v=lLfafNPVs5g) on the new version. 

## Creating an adapter in 1.x

Initially, there was 4 overloads of `adapt`, each offering various possibilities:

- `adapt(path, initialState)`
- `adapt([path, initialState], adapter)`
- `adapt([path, initialState], sources`
- `adapt([path, initialState, adapter], sources)`

While the array syntax is consise and helps to reduce lines of code, having four overloads comes with a little bit of trouble:

- If you are doing something wrong, TypeScript might not be of a great help because of that, outputing confusing error messages
  ![Error example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y3ebn0g35woe1lv8x0zv.png)
- Creating a new adapter when joining in a project and not having prior experience with StateAdapt could also be a bit frustrating until you get used to the syntax

Let's see how we called an adapter previously in the first version of StateAdapt:

- Single value

```ts
const name = adapt('name', 'John Doe');
```

- With an adapter

```ts
const name = adapt(['name', 'John Doe'], {
  uppercase: name => name.toUpperCase(),
  selector: {
    firstLetter: name => name.at(0),
  },
});
```

- From a source

```ts
const name = adapt(
  ['name', 'John Doe'],
  http.get('/name').pipe(toSource('[Name] Get from HTTP')),
);
```

- With an adapter and a source

```ts
const nameAdapter = createAdapter<string>()({
  uppercase: name => name.toUpperCase(),
  selector: {
    firstLetter: name => name.at(0),
  },
});

const name = adapt(['name', 'John Doe', nameAdapter], {
  set: http.get('/name').pipe(toSource('[Name] Get from HTTP')),
});
```

## What v2 is about

Well aware of the issues induced by the plurality of the `adapt` API, Mike Pearson opened a issue about this, with the goal of discussing how to unify the four overloads into a single one: {% embed https://github.com/state-adapt/state-adapt/issues/45 %}

As a result, **`adapt` now only requires an initial value** and a second, optional, configuration object takes care of specifying any additional behaviour.

> This also means that this version is a breaking change, hence the bump in the major digit

### Usage and migration

Let's see how this migration will impact the current code:

- Single value

```ts
// v1
const name = adapt('name', 'John Doe');

// v2
const name = adapt('John Doe', {
  path: 'name',
});
```

> Optionally, since the configuration object is optional, defining a path is no longer required:
> ```ts
const name = adapt('John Doe');
```

- With an adapter

```ts
// v1
const name = adapt(['name', 'John Doe'], {
  uppercase: name => name.toUpperCase(),
  selector: {
    firstLetter: name => name.at(0),
  },
});

// v2
const name = adapt('John Doe', {
  path: 'name',
  uppercase: name => name.toUpperCase(),
  selector: {
    firstLetter: name => name.at(0),
  },
});
```

- From a source

```ts
// v1
const name = adapt(
  ['name', 'John Doe'],
  http.get('/name').pipe(toSource('[Name] Get from HTTP')),
);

// v2
const name = adapt('John Doe', {
  path: 'name',
  sources: http.get('/name').pipe(toSource('[Name] Get from HTTP')),
});
```

- With an adapter and a source

```ts
// v1
const nameAdapter = createAdapter<string>()({
  uppercase: name => name.toUpperCase(),
  selector: {
    firstLetter: name => name.at(0),
  },
});

const name = adapt(['name', 'John Doe', nameAdapter], {
  set: http.get('/name').pipe(toSource('[Name] Get from HTTP')),
});

// v2
const name = adapt('John Doe', {
  path: 'name',
  // 👇 If needed, the adapter can be created locally
  adapter: {
    uppercase: name => name.toUpperCase(),
    selector: {
      firstLetter: name => name.at(0),
    },
  },
  sources: http.get('/name').pipe(toSource('[Name] Get from HTTP')),
});
```

> We can still define the adapter elsewhere and use it only when calling `adapt`:
> ```ts
const nameAdapter = createAdapter<string>()({
  uppercase: name => name.toUpperCase(),
  selector: {
    firstLetter: name => name.at(0),
  },
});
>
const name = adapt('John Doe', {
  path: 'name',
  // 👇 Using an existing adapter
  adapter: nameAdapter,
  sources: http.get('/name').pipe(toSource('[Name] Get from HTTP')),
});
```

### Personal thoughts

I discovered StateAdapt a couple of months ago through a video of [Joshua Morony](https://www.youtube.com/c/JoshuaMorony) and it gained my interest.

However, after diving into it, I was quickly lost between the overloads of the library, and it was making the shift from other state management libraries such as NgRx harder.

With this update, I find the unified synthax way more accessible, and easier to get started with.

Mike Pearson is doing an awesome work with this and I hope StateAdapt will continue to grow!

If you would like to give it a try, he has [a Youtube channel](https://www.youtube.com/@mikepearsonengineering8793/videos) full of resources, and a great overview of the new version:

{% embed https://www.youtube.com/watch?v=lLfafNPVs5g %}

If you wish to migrate, he also recorded [a step by step migration example](https://www.youtube.com/watch?v=lLfafNPVs5g) using all the overloads.

---

I hope you learned something useful there!

{% embed https://dev.to/pbouillon %}

---

> Cover image from [StateAdapt's website](https://state-adapt.github.io)