How to sync the FULL config between server and client side state

TLDR: statestore is for overriding Node-RED config, and the updated (including dynamic updates) config is sent on the ui-config event. The datastore tracks msg objects, generally only the last received message, but can support a history of messages if required using datastore.append in the case of charts (as an example).

Server-Side Stores

Docs: State Management | Node-RED Dashboard 2.0

  • datastore: A map of each widget to the latest msg received by a respective node in the Editor.
  • statestore: A store for all dynamic properties set on widgets (e.g. visibility or setting a property at runtime). Often, these values are overrides of the base configuration defined in Node-RED. (This is our equivalent of FlexDash's state store.)

The pattern we've implemented in core is that the statestore is populated when a node receives messages, and detects a property in that message that it is interested in, e.g in ui-button.js:

Loading Events

Docs: Events Architecture | Node-RED Dashboard 2.0

UI Config/State

When the Dashboard is first loaded on a page refresh or navigation, we send a ui-config event, which details every widget, page, theme, etc. This contains the specification for each node (which is the Node-RED config schema), but with the relevant values overriden by the widget's statestore.

This is the single source of truth for the state of a widget at that time.

When building your own node, in the Node-RED .js file, copy the pattern seen in the buttons above. To access the stores from within a third-party node, you can call:

const group = RED.nodes.getNode(config.group)
const base = group.getBase()
base.stores.<data/state>

I don't have an example in ui-example node for dynamic properties yet, so I'll try and get something added.

Anything you put into the statestore will be merged with the config of your node and transmitted for ui-config.

Widget Load

For each widget, when that itself loads, we send a widget-load(msg) event, which passes the latest received msg from the datastore.

The widget does not need to do anything with this if it's not appropriate. The dynamic/updated state would already have been sent to the widget from the statestore via the merged ui-config schema.

Updates Required

  • I had a mis-typed statement in the statestore description, which claimed " Often, these values are overrides of the base configuration found in the datastore.". This is not true, and I corrected above, it should read: "Often, these values are overrides of the base configuration defined in Node-RED. "
  • On my side, I do need to update the "Input" events architecture diagram to show an example of the Dynamic Properties approach we now have

Ideas for Improvement:

Alongside the msg being sent in widget-load, I could also pass the config? It's already sent with the ui-config event, but for peace of mind, it's a lightweight addition, that makes sense to include?

1 Like