question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Redux: actions have inconsistent payloads and/or huge payloads, state has circular references, ReduxDevTools crashes

See original GitHub issue

Bug Report

Describe the Bug

Recently I’ve encountered the following problems in how Redux is used in the OHIF viewer and libraries:

  • Some of the Redux actions dispatched in the viewer have inconsistent payloads. That is, an action of a given type may have data layout completely different from another action of the same type. This is unsafe and violates the very basic principles of Redux: clients consuming these actions (whether OHIF or third party) cannot count on receiving a particular piece of data anymore. This leads to frustration and breakage.

  • Some of the actions have payloads that are way too big for any real-world use

  • Some actions result in Redux state having circular references in its object layout

  • Redux Dev Tools are slow and may crash unpredictably

What steps can we follow to reproduce the bug?

  • Launch a chromium-based browser and install latest version of Redux Dev Tools (2.17.0 as of today)

  • Open Chrome Dev Tools and switch to “Redux” tab

  • Open, for example, https://viewer.ohif.org/viewer/1.2.826.0.13854362241694438965858641723883466450351448

    Patient Name: Dummy MRN: 716713622818558421526494572171041020891020924597 Study: 1.2.826.0.13854362241694438965858641723883466450351448

  • Click on first VIEWPORT::SET action in actions sidebar. Click “Action” tab. You will see that this action dispathes the following object:

{
  type: 'VIEWPORT::SET',
  viewportIndex: 0,
  data: {
    displaySetInstanceUid: '7d860e86-cb2f-2758-4a55-a8815e93e311',
    studyInstanceUid: '1.2.826.0.13854362241694438965858641723883466450351448',
    currentImageIdIndex: 0,
    sopInstanceUid: '1.2.826.0.13567014206729755965107989533871182029618726'
  }
}

Click on next VIEWPORT::SET action in actions sidebar. You will see that this action has completely different data:

{
  type: 'VIEWPORT::SET',
  viewportIndex: 0,
  data: {
    plugin: "cornerstone",
    dom: "<120k lines of json which make my computer hang for a minute>"
  }
}
  • Further, scroll slices in the viewport, try to drag the “SEG” series onto the viewport. RESET_LABELLING_AND_CONTEXT and more VIEWPORT:SET actions will be dispatched. At some point Redux Dev Tools will stop responding to user interaction and the popup “Redux DevTools has crashed” will appear:

Screenshot of the popup "Redux DevTools has crashed"

  • If one is quick enough, before dev tools crash, they may be able to catch the following pink elephants in state snapshots on “State” tab (note '[CIRCULAR]'):
{
 ...
 viewports: '[CIRCULAR]',
 ...
}

{
  ...
  viewports: {
    activeViewportIndex: 0,
    layout: {
      viewports: [
        {
          height: '100%',
          width: '100%',
          plugin: 'cornerstone'
        }
      ]
    },
    viewportSpecificData: {
      '0': {
        plugin: 'cornerstone',
        dom: '[CIRCULAR]',
        displaySetInstanceUid: '70ade2ad-d07e-c252-49c8-0d212aacba16',
        studyInstanceUid: '1.2.826.0.13854362241694438965858641723883466450351448',
        currentImageIdIndex: 0,
        sopInstanceUid: '1.2.826.0.11521735486436309604024223829282084628708383',
        seriesDate: '20190129',
        seriesTime: '153943',
        seriesInstanceUid: '1.2.826.0.53608788865337544170015089925954063493496832',
        seriesNumber: 1001,
        seriesDescription: 'White matter hyperintensity segmentation',
        numImageFrames: 48,
        modality: 'SEG',
        isMultiFrame: true,
        instanceNumber: 1,
        sopClassUids: [
          '1.2.840.10008.5.1.4.1.1.66.4'
        ],
        isClip: true
      }
    }
  },
  ...
}

Possible resolution strategies

  • Consider adopting a stable action layout. Action creators shoud be made more type safe. It is not enough to just pass data to action creator and to insert data into the action object as is. Every field of data should be specified separately to ensure that there is no divergence in data type. Example:
  function setViewportData(data) {
    return  {
      type: 'VIEWPORT:SET',
      // data can be anything
      data
    };
  }

  dispatch(setViewportData({ dom: myEntireDom })  // uh-oh! Where is my studyId?

  function setViewportData(studyId, seriesId) {
    return  {
      type: 'VIEWPORT:SET',
      // data can only be an object containing studyId, seriesId. both can be anything
      data: {
        studyId,
        seriesId,
      }
    };
  }

  dispatch(setViewportData({ studyId: '1.2.3', seriesId: undefined })) // better, but still
  • Consider adopting Flux Standard Action pattern

  • Consider switching relevant parts of the application and libraries to Typescript (see e.g. @babel/preset-typescript). Introducing types and type-safe actions, e.g. via typescript-fsa, will be a major improvement in terms of usability of these actions and of 3rdparty-developer experience. Example:

  interface SetViewportDataParams {
    studyId: string;
    seriesId: string;
  }

  function setViewportData({ studyId, seriesId }: SetViewportDataParams) {
    return  {
      type: 'VIEWPORT:SET',
      // data can only be an object containing studyId and seriesId, both are strings
      data: { 
        studyId,
        seriesId,
      }
    };
  }

  dispatch(setViewportData({ dom: myEntireDom })) // compiler error

  dispatch(setViewportData({ studyId: '1.2.3', seriesId: undefined })) // compiler error

  dispatch(setViewportData({ studyId: '0.1.23', seriesId: '4.5.678' })) // okay
  • Consider using Redux Dev Tools more during development process in order to verify actions payloads and state changes. Developers typically expect Redux Dev Tools to be functioning on any project. It is frustrating when they are not: “What do I do now? console.log everything?”

  • Consider adding more unit tests for component logic

  • Should DOM objects be a part of the action payload? I think this particular dom entry is way to big to be useful

  • Consider making eslint config more strict. There are eslint rules that may help to catch bugs in redux

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:2
  • Comments:7 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
dannyrbcommented, Oct 8, 2019

I believe we have very similar goals then ^_^

There is an in-progress PR here that adds some of that functionality to react-cornerstone-viewport, or at least makes it a bit cleaner: https://github.com/cornerstonejs/react-cornerstone-viewport/pull/41

Where you can now set imageIdIndex for the component, and it will scrollToIndex. The API changes we made there are a large part of why I am now touching the VIEWPORT actions as I integrate the updates.

I’m torn on adding more data to Redux. It’s a bit odd to add everything we need for every possible viewport implementation, overlay, etc. If we have key bits of information that should cause the view to change, like a studyId, then we should include that – but then that studyId can be used to query a service for other data that doesn’t need to be reactive.

I’m all ears here, as I’m new-ish to React development. If you have any specific problems that are not yet solved, or suggestions around a cleaner handshake, we could set up a quick call?

1reaction
dannyrbcommented, Oct 7, 2019

@ivan-aksamentov, thank you for the detailed write-up! Internally, we’re aware that entirely too much data is being jammed into Redux. We’re actively monitoring new PRs to make sure we don’t continue this creep, and we’re working to simplify this portion of the code base.

A large part of the problem is that the bulk of these changes were made as part of a massive refactor to use React. It’s common for new React developers to want to make everything reactive. Now that we’re here, our appetite for large refactors has waned, so we’re trying to clean these things up as we touch relevant portions of the code base.

Viewports in particular are hairy because they’re an “extension point”, with each exposed viewport module potentially requiring different data in order to function. We’re working to more clearly define the responsibilities of core, viewer and viewport extensions in this handshake. Currently, I believe we’re leaning toward information like: ids, layout, etc. continuing to exist in Redux, while core would expose the remaining data (and DICOMWeb / API calls, cached image data, etc) through services, some of which we might expose to extensions.

The path’s not super clear yet, but please feel free to weigh in or draft reports of other problems/solutions as you encounter them 🙏

Read more comments on GitHub >

github_iconTop Results From Across the Web

Redux Dev Tools not working for large action payload
With this line, it fails. The problem appears to be "data:action.data.rows". rows is an array and if I change this to return an...
Read more >
Use sanitizers to avoid Redux Devtools crash
The solution. The idea here is that actions and states can be replaced by something else manually, so it does not fix the...
Read more >
All the Electron Docs! | Electron
Electron is an open source library developed by GitHub for building cross-platform desktop applications with HTML, CSS, and JavaScript.
Read more >
Troubleshooting | Redux
This is a place to share common problems and solutions to them. ... It is tempting to modify the state or action passed...
Read more >
Accelerator - Electron - W3cubDocs
Emitted when the renderer process of webContents crashes or is killed. ... all developers working with Electron, both big and small in team-size,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found