Unidirectional data flow in plugins?

I am trying to organize my plugin’s data flow in unidirectional fashion, like so:

  1. In handleKeyDown, I convert key presses to plugin actions, where I define a plugin action to be the payload of a transaction’s meta.
  2. In StateField apply, I extract the action from the transaction and use it to reduce my plugin’s state, obtaining a new plugin state.
  3. In PluginSpec view, I update the plugin-managed view with the plugin state from step 2.

All this works fine provided that the plugin is only interested in updating its own state. What I can’t figure out is how to update editor state within the separation of responsibilities outlined above. Specifically, what I would like to be able to do, is be able to emit editor state updates from within step 2 above, which I think is the cleanest place to do this in. But StateFieldapply receives no reference to the view, which I would need in order to request an editor state update.

I suspect that the above scheme, heavily informed by Redux, may be unnatural to the way ProseMirror plugins are intended to work, but I’m not quite sure what that is. Any suggestions for improvement would be appreciated.

But StateField’ apply receives no reference to the view, which I would need in order to request an editor state update.

You have a reference to the state, which is usually where editor state lives. The view is just a shallow wrapper around its current state.

If I understand what you’re trying to do correctly, you’ll want to fire a transaction which updates both general editor state (document or selection) and your plugin state (via transaction metadata).

I suspect that the above scheme, heavily informed by Redux, may be unnatural to the way ProseMirror plugins are intended to work

No, that’s exactly how they are intended to work (apply methods are basically reducers).

Yes, I’m trying to do something like this:

handleKeyDown() {
    view.dispatch(transactionWithMeta("myAction")) // all good
}
apply(tr, pluginState, oldState, newState) {
    // fire off a new transaction without reference to view?
    return newPluginState
}

StateField apply does have a reference to editor state but I thought that the only way to change the current state is via EditorView.updateState or dispatch. Either way, I thought that I would need a reference to the view in order to persist changes to state.

I think I might be missing something very simple here…

The idea is to create the original transaction in such a way that it includes those editor state changes. Redux reducers also don’t fire off new actions.

Yes, that was it. Thank you.