How I got selectors in Redux Devtools
Tags: stateadapt, redux, ngrx
Published: 2022-11-10 07:34:50

---
title: How I got selectors in Redux Devtools
published: true
description: Redux Devtools is nice for inspecting state. But sometimes it would be nice to be able to inspect derived state too.
tags: stateadapt,redux,ngrx
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ex7h0uc75jzh5xbyx8o.png
# Use a ratio of 100:42 for best results.
# published_at: 2022-11-07 06:54 +0000
---

[YouTube](https://www.youtube.com/watch?v=iWA9HGjaw7g)

Redux Devtools is nice for inspecting state. 

But sometimes it would be nice to be able to inspect derived state too. 

It's not a good practice to put derived state in the store. But my solution doesn't put it in the store; it puts it in Redux Devtools only, using the `stateSanitizer` option. 

## It started with a different problem

Selectors have always bugged me. The syntax is so long. I mean, look at this:

```tsx
const selector = createSelector(
  selectThing1,
  selectThing2,
  (thing1, thing2) => doCalculation(thing1, thing2),
);
```

If we didn't need that memoized, in React we could just have

```tsx
const thing3 = doCalculation(thing1, thing2);
```

Why should a performance optimization impact our code so much?

So, I wanted to redesign syntax for selectors from the ground up: If I could magically have the most ideal syntax for creating selectors, what would it look like? 

![Selector syntax file diff](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkfp83tzcr6m0fg6pgs7.png)
<figcaption>Left: Reselect. Right: What I ended up with</figcaption>
<figcaption>.</figcaption>

 

First, I thought it would be nice if I could just define the function itself, and have what I'm accessing automatically detected and memoized for me. But how do we detect when code is accessing certain state, and attach extra behavior to it? Well that would be a JavaScript `Proxy`. So could I define my selector like this?

```tsx
const selectors = new Proxy(...);
// ...
selectors.getThing3 = selectors.getThing2 + selectors.getThing1;
```

What about TypeScript? And how do we use this selector in future selectors in a reliable way?

I ended up deciding on a builder pattern, with a function that could be called again and again, sort of like this:

```tsx
const selectors = buildSelectors<string>({
  reverse: state => state.split('').reverse().join(''),
})({
  isPalendrome: s => s.reverse === s.state,
})({
  // etc...
})() // End
```

`selectors` would then be an object with everything in it, and typed correctly.

This is similar to how [Redux Toolkit](https://redux-toolkit.js.org/api/createEntityAdapter) and [NgRx/Entity's](https://ngrx.io/guide/entity) state adapters have one big object with a lot of state management utilities all together. The exact syntax I ended up was inspired by them:

```tsx
const adapter = buildAdapter<State>()()({ // Sorry
  reverse: s => s.state.split('').reverse().join(''),
})({
  isPalendrome: s => s.reverse === s.state,
})({
  // etc...
})() // End
```

So, why `s`? Well, should the object (`s`) passed into each selector function be named `selectors`, `state` or `selectorState`? In reality it's just a proxy, so none of these really make sense. So, the convention is `s`, since it's short and the only letter all the possible meanings share.

Also, since we're treating the selectors as if they were just state, I named them nouns. It's much less awkward than verbs like `getIsPalendrom: s => s.getReverse === s.getState`.

So far, this is nice.

But there's another issue I have with standard Redux/NgRx: Reusability. With standard Redux/NgRx, if we want to reuse selectors for different instances of state, we can't just call `createSelector`; we have to create selector creators. Here's what that looks like:

```tsx
import { createSelector } from 'reselect'; // or whatever

// Need a function that returns the selector in order to be
// reusable and independently memoized:
const getSelectReverse = (selectState: (state: any) => string) =>
  createSelector(selectState, state => state.split('').reverse().join(''));

const getSelectIsPalendrome = (selectState: (state: any) => string) =>
  createSelector(
    selectState,
    getSelectReverse(selectState),
    (state, reverse) => state === reverse
  );

// ...
// Before using for some specific state
const selectReverse = getSelectReverse(selectSpecificState);
const selectIsPalendrome = getSelectIsPalendrome(selectSpecificState);
```

Wow, that sucks.

Let's look again at the syntax I think is ideal: 

```typescript
import { buildAdapter } from '@state-adapt/core';

const stringAdapter = buildAdapter<string>()()({
  reverse: s => s.state.split('').reverse().join(''),
})({
  isPalendrome: s => s.reverse === s.state,
})();
```

That's better. 

But is it possible?

## The hard part: implementation

I implemented this syntax in my state management library, [StateAdapt](https://state-adapt.github.io/). _StateAdapt_ is a state management library based on the state adapter pattern introduced by [NgRx/Entity's](https://ngrx.io/guide/entity) `entityAdapter`. 

The implementation is fairly straight-forward. For each new selector in each selector block, create a new function that will

- Call the developer-defined function but pass in a proxy instead of state
- In the proxy `get` handler, use the name of the selector to call each actual selector function, passing in `state`
- Cache the results from each "input" selector
- Before each run, check if there are any changes with the input selectors and their cached values

Here's a cool benefit from this: Since selectors are deterministic, if only 1 selector was accessed last time and it returns the same value this time, we don't need to call the function again. For example, if we had a selector defined like this:

```tsx
  something: s => s.thing1 || s.thing2 || s.thing3,
```

and `s.thing1` always returned something truthy, then it would never matter what `thing2` or `thing3` would return, so we never call them. With traditional memoized selectors, all input selectors have to be called in advance. This could cause unnecessary calls to `thing2` and `thing3`, and if those ever returned anything different, the main selector function would need to run again as well; they're unnecessary work themselves, but they can also trigger unnecessary work in our new selector. With proxies, we can avoid all of that!

Isn't that cool?

## The problem

But there's actually a big problem with this. 

Each of these selector functions is getting created in the same context. That means when our proxy caches the input selector values, it caches them for every use of that selector. So if we try to use the selector on two entirely different states, memoization would only occur when the states happen to be the same, which may never happen.

I really struggled to find a solution to this, because I loved the syntax I had, and everything I thought of for the first whole day required changing the API I created.

The first possibility was to define each selector block in a function:

```tsx
const stringAdapter = buildAdapter<string>()()(() => ({
  reverse: s => s.state.split('').reverse().join(''),
}))(() => ({
  isPalendrome: s => s.reverse === s.state,
}))();
```

This would allow me to define the blocks lazily with a context and cache object dedicated to each specific instance of state. 

But that syntax collided with other functionality I already implemented, so I quickly moved on to something even more annoying:

```tsx
const stringAdapter = () => 
  buildAdapter<string>()()({
    reverse: s => s.state.split('').reverse().join(''),
  })({
    isPalendrome: s => s.reverse === s.state,
  })();
```

Now I could build the entire adapter lazily and have a cache object dedicated to each specific instance of state.

But what is this thing? A state adapter creator? Do we have to get annoying with the names? And Prettier insisted on defining it with the last line indented, so that would be annoying for people using VSCode like me when they press Enter and VSCode automatically indents the cursor 1 level. I hate that. 

Next I tried something else: Have each selector be a selector creator, similar to traditional memoized selectors:

```tsx
const stringAdapter = buildAdapter<string>()()({
  reverse: () => s => s.state.split('').reverse().join(''),
})({
  isPalendrome: () => s => s.reverse === s.state,
})();
```

I was pretty happy with this, but I really, _really_ hate superfluous syntax, and it just hurt me to force everyone using [StateAdapt](https://state-adapt.github.io/) (all 10 of them) to write an extra `() => ` thousands of times. Not to mention a breaking change in _StateAdapt_'s API.

But this got me closer to what ended up working.

When I want to call a selector for a specific piece of state, I pass in a cache object as the 2nd argument. There will still only be 1 selector function used for all instances of state, but it uses the cache object passed to it, and passes it along to the selector functions above. 

This means I need a cache object dedicated to each piece of state. Then when I use a state adapter, I just define a new object with all the same selectors, but with the cache object passed in the 2nd argument, so the new selectors just need to pass in `state` and the correct cache object is implicitly used. 

But wait... Now we have a big cache object with all the return values of all selectors used for a piece of state... That's pretty convenient!

## Selectors in Redux Devtools!

So here is how we can now get selectors into Redux Devtools:

1. Assemble each reducer's selector cache into one giant, global selector cache
2. Create a serializer that converts the cache into a simple object that will be easy to read in Redux Devtools
3. Attach the global cache object to the `window` object
4. Create a custom state sanitizer for Redux Devtools that accesses the global cache object, serializes it, and adds it as another property onto state

There's one caveat: Since the selectors are evaluated lazily, when they show up in Redux Devtools, the cached values will be based on the previous state. So, I named them `_prevSelectors` in [StateAdapt](https://state-adapt.github.io/).

## Using this with NgRx or Redux

You can import the `stateSanitizer` from `@state-adapt/core` and use this with _Redux_ or _NgRx_ today:

```tsx
import { stateSanitizer } from '@state-adapt/core';

// Redux
const store = createStore(
  rootReducer,
  window.__REDUX_DEVTOOLS_EXTENSION__?.({ stateSanitizer }),
);
// Or however you pass in the stateSanitizer with your flavor of Redux

// NgRx
    StoreDevtoolsModule.instrument({
      // ...
      stateSanitizer,
    }),
```

Whatever selectors you define using `buildAdapter` will be possible to have shown in Redux Devtools. But there's an extra function we need to call to connect the selectors to global state, called `mapToSelectorsWithCache`:

```tsx
import { buildAdapter, createSelectorsCache, mapToSelectorsWithCache } from '@state-adapt/core';

// ...

const stateAdapter = buildAdapter<State>()()({
  // ...
})();

// ...

// Each state slice will be memoized independently:

const getState1 = (state: any) => state.childState1 as State;
export const cache1 = createSelectorsCache();
const state1Selectors = mapToSelectorsWithCache(
  stateAdapter.selectors,
  getState1,
  cache1,
);

const getState2 = (state: any) => state.childState2 as State;
export const cache2 = createSelectorsCache();
const state2Selectors = mapToSelectorsWithCache(
  stateAdapter.selectors,
  getState2,
  cache2,
);
```

This is easier in [StateAdapt](https://state-adapt.github.io/), by the way :)

Now, just like you have a root reducer in _Redux_ and _NgRx_, you will have a root selectors cache. But you import this from StateAdapt and attach your feature caches as children (it's a recursive structure):

```tsx
import { createSelectorsCache, globalSelectorsCacheKey } from '@state-adapt/core';
import { cache1, reducer1, cache2, reducer2 } from './child-feature/reducer';

const cacheChildren = (window as any)[globalSelectorsCacheKey]?.__children;
cacheChildren.childState1 = cache1;
cacheChildren.childState2 = cache2;

export const reducer = combineReducers({
  childState1: reducer1,
  childState2: reducer2,
});
```

And that's it!

Here's a working demo remo you can check out: [Cart Demo](https://github.com/mfp22/react-redux-stateadapt-ecommerce)

## Joined Selectors

All of this works if you have selectors that only select from a single slice of state. What if you want to combine selectors across multiple slices of state?

Well, in _Redux_ and _NgRx_ you can always just use a regular selector. It won't use global cache obviously, but it will function as expected anyway:

```tsx
const selectAllArePalendromes = createSelector(
  state1Selectors.isPalendrome,
  state2Selectors.isPalendrome,
  (...palendromes) => palendromes.every(p => p),
);
```

In [StateAdapt](https://state-adapt.github.io/), each `reducer` is called a "smart store", and you can combine state from multiple smart stores like this (example from Angular):

```tsx
store1 = adapt(['string1', 'racecar'], adapter);
store2 = adapt(['string2', 'racecar'], adapter);

allArePalendromes$ = joinStores({
  string1: this.store1,
  string2: this.store2,
})({
  allArePalendromes: s => s.string1IsPalendrome && s.string2IsPalendrome,
})().allArePalendromes$;
```

This syntax isn't as minimal as it could be, but it mirrors syntax for a function called `joinAdapters` so it could be easy to restructure state if necessary. Minimalism shouldn't always the top concern. 

Anyway, with some fun code I was able to get these to show up in Redux Devtools too. They show up as children of both input stores, unfortunately using the cache ID as the key instead of a meaningful string. But at least they're there!

![Joined Selectors](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qd22sid17fuwj8f9m5m.png)

## Conclusion

I didn't know I was going to end up putting selectors in Redux Devtools when I started trying to figure out how to make selectors in adapters reusable and independently cached. But I'm glad I did! Selectors contain a lot of important business logic, and this is one more instance where we can potentially save a lot of time by not having to set breakpoints in code whenever a selector outputs something unexpected.

I hope you try out `buildAdapter` and give me some feedback. [StateAdapt](https://state-adapt.github.io/) is getting very close to a 1.0 release. While it has been used in dozens of projects now, and it has automated tests covering every bug found to date, there is still a chance of something missing that will need to be addressed before 1.0. But it shouldn't take longer than a couple of weeks.

And if you like `buildAdapter`, you might as well try using the rest of [StateAdapt](https://state-adapt.github.io/) on a feature. Once you have an adapter, plugging it into a smart store is extremely easy:

```tsx
adapter = buildAdapter<State>(...);
store = adapt(['statePath', initialState], this.adapter);
state$ = store.state$; // Observable of store's state
```

So give it a try and give [the repo](https://github.com/state-adapt/state-adapt) a star if you like it!

Let me know how it goes!