Debug Ionic Application (NGRX) using Redux remote dev tools
Tags: ionic, angular, redux, ngrx
Published: 2023-02-17 02:11:57

Recently I was working on Ionic app that was built with Angular. The application uses NGRX for state management. I wanted to debug the state (i.e inspect actions, and state ..etc) while application is running on emulator. I expected that by simply running redux dev tools from chrome that it will magically work out of the box, and straight away, but unfortunately this was not the case.

Looking around for sometime I stumbled upon a precious [article](https://medium.com/nextfaze/remote-debugging-ngrx-store-with-ionic-74e367316193) that was explaining how to put everything together.

Unfortunately the article is outdated, but through some further search and playing around, I managed to make everything work.

I thought to share details here to help anyone who might be looking for a solution to same exact issue.

- Step 1:
install [NGRX store dev tools](https://ngrx.io/guide/store-devtools/install)

- Step 2:
Create 2 files to handle connection between your application and Redux remote dev tool

File1: 
`remote-devtools-connection.ts`

```
import { ReduxDevtoolsExtensionConnection } from '@ngrx/store-devtools/src/extension'

export class RemoteDevToolsConnectionProxy implements ReduxDevtoolsExtensionConnection {
  constructor(public remotedev: any, public instanceId: string) {}
  init(state?: any): void {
    console.log('Socket was initiated', state)
  }
  error(anyErr: any): void {
    console.error('Socket Error', anyErr)
  }

  subscribe(listener: (change: any) => void): any {
    const listenerWrapper = (change: any) => {
      listener(change)
    }

    this.remotedev.subscribe(listenerWrapper)
    // Hack fix for commit/time-travelling etc. if the devtools are already open
    setTimeout(() => listenerWrapper({ type: 'START' }))
  }

  unsubscribe(): any {
    // HACK fix bug in @ngrx/store-devtools that calls this instead of returning
    // a lambda that calls it when their Observable wrapper is unsubscribed.
    return () => this.remotedev.unsubscribe(this.instanceId)
  }

  send(action: any, state: any): any {
    this.remotedev.send(action, state)
    // Never called
  }
}

```

File2 (depends on File1): 
`remote-devtools-proxy.ts`

```
import {
  ReduxDevtoolsExtension,
  ReduxDevtoolsExtensionConfig,
  ReduxDevtoolsExtensionConnection
} from '@ngrx/store-devtools/src/extension'
import { connect } from 'remotedev/lib/devTools'

import { RemoteDevToolsConnectionProxy } from './remote-devtools-connection'

export class RemoteDevToolsProxy implements ReduxDevtoolsExtension {
  remotedev: any = null
  defaultOptions = {
    realtime: true,
    autoReconnect: true,
    connectTimeout: 20000,
    ackTimeout: 10000,
    secure: true
  }

  constructor(defaultOptions: Object) {
    this.defaultOptions = {
      ...this.defaultOptions,
      ...defaultOptions
    }
    console.log('Socket config', this.defaultOptions)
  }

  connect(
    options:
      | {
          shouldStringify?: boolean
          instanceId: string
        } & ReduxDevtoolsExtensionConfig
  ): ReduxDevtoolsExtensionConnection {
    const connectOptions = Object.assign(this.defaultOptions, options)

    this.remotedev = connect(connectOptions)

    const connectionProxy = new RemoteDevToolsConnectionProxy(
      this.remotedev,
      connectOptions.instanceId
    )
    return connectionProxy
  }

  send(action: any, state: any): any {
    this.remotedev.send(action, state)
  }
}

```

File3 (depends on File2): make sure to include this file in your app based on your environment setup. Like direct import, or webpack include .. etc

The variables `REMOTE_DEV_HOST` & `REMOTE_DEV_PORT` can be environment dependent, or hardcoded. You can hardcode them at least to make sure everything works as expected. However the most important point it to use "EXACT" same IP, and PORT in all following steps

`index.ts`
```
import { RemoteDevToolsProxy } from './remote-devtools-proxy'

declare const REMOTE_DEV_HOST: string
declare const REMOTE_DEV_PORT: number

if (!window['devToolsExtension'] && !window['__REDUX_DEVTOOLS_EXTENSION__']) {
  console.log('!!!!!! REDUX INSPECTION HAS STARTED !!!!!')
  const remoteDevToolsProxy = new RemoteDevToolsProxy({
    // Needs to match what you run `remotedev` command with and
    // what you setup in remote devtools local connection settings
    hostname: REMOTE_DEV_HOST,
    port: REMOTE_DEV_PORT,
    connectTimeout: 300000, // extend for pauses during debugging
    ackTimeout: 120000, // extend for pauses during debugging
    secure: false // dev only
  })

  // support both the legacy and new keys, for now
  window['devToolsExtension'] = remoteDevToolsProxy
  window['__REDUX_DEVTOOLS_EXTENSION__'] = remoteDevToolsProxy
} else {
  // handle case outside real device, or emulator
}

```

- Step 3:
in your angular module make sure you include store dev module
```
@NgModule({
  imports: [CommonModule, StoreDevtoolsModule.instrument({ maxAge: 100 })]
})
```
- Step 4:
install [@redux-devtools/cli](https://www.npmjs.com/package/@redux-devtools/cli)

- Step 5:
add a script to start redux devtools server which will listen to state, and action changes in your package.json

```
scripts:
{"remote-dev":"redux-devtools --hostname=ADD_IP_FROM_PREVIOUS_STEPS --port=ADD_PORT_FROM_PREVIOUS_STEPS --wsEngine=ws --logLevel=3"}
```

and run it in a separate terminal tab/window

- Step 6:
You may get some error about global is not defined, and in this case add following to polyfill.ts (this is generated by default in your root folder) in your Angular app (more info can be found [here](https://github.com/angular/angular-cli/issues/9827#issuecomment-369578814))

`polyfill.ts`

```
(window as any).global = window
```

- Step 7:
open chrome, and right click redux extension to open remote dev tools

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o84fc33m13t75okk7arg.png)

- Step 8:
connect to your host from redux remote settings (UX is bad as there is no feedbad if connection succeeds, or fails). Howevwer ater successful connection you should see the store visible in your dev tools

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u773gxtyn6kq6vxsdrqe.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88rgtjmdg2ca9m1kdmm9.png)


Happy Hacking ...