lightning
lightning copied to clipboard
Migrating Workflow Edit to React
Proposed Approach
- Single React Root • We will maintain one global React <App /> (or similar root component) that is initialized at page load. • This <App /> will host a shared store (e.g., Zustand or Redux), ensuring all embedded React components can access the same global state and context providers.
- Use React Portals for Incremental Embedding • For each LiveView-rendered container or “sub-view” we want to replace, we will add a small Phoenix LiveView Hook that notifies our React root once the container is mounted. • The root <App /> will dynamically create a portal into that container, rendering the React component (e.g., “InputTab” or “Inspector”) inside the LiveView-generated DOM.
- Gradual Migration • We will handle each subcomponent (or tab) one at a time to reduce risk. • The simplest components (like the “Input Tab”) will be migrated first, verifying our portal approach works smoothly. • More complex areas, such as the Inspector and multi-user code editing, will come later and build on this pattern.
- Maintain Real-Time Features • Where we currently use LiveView event handlers, we will transition to dedicated Phoenix channels or minimal LiveView stubs to broadcast real-time updates. • Our React components will consume those events (and handle concurrency or presence as needed), while LiveView focuses on general page layout and server-driven updates that we don't need to replace.
- Long-Term Vision • Once we complete the incremental migration, the main UI for editing workflows will be React-based, with LiveView primarily for top-level page rendering. • This architecture also positions us to adopt collaborative editing libraries and use some of the many React libraries out there for a better UX.
Outcome / Success Criteria
• Consistent React UI: Each migrated sub-view fully rendered and controlled by React. • Shared State: Components share a single store (or set of stores) for data. We avoid duplicating logic between client and server for form handling/validation. • Reduced LiveView Complexity: Less “bridge code,” fewer LiveView event handlers for pure UI updates. LiveView remains for initial rendering and limited server-driven events. • Maintain or Improve Performance: The portal approach must be responsive and must not introduce significant overhead or degraded user experience. • Position for Collaboration: Our approach should be compatible with future CRDT or yjs integration.
Sub-issues
- [ ] Input Tab
- Replace the LiveView-based form for creating/selecting input dataclips with a React <InputTab /> component.
- Integrate JSON validation and any future Monaco editor enhancements.
- [ ] Run Pane
- Migrate the Run Pane (tabs, step list, log viewer integration) to React.
- Retain real-time log streaming via a Phoenix channel.
- [ ] AI Assistant
- Convert the LiveView AI chat tab to React, including session management and message rendering.
- Potentially refactor Apollo calls to be handled directly in React or via a channel.
- [ ] Editor Shell
- Rebuild the “3-pane” Job Editor layout (Input, Editor, Run) as a React-managed layout.
- Ensure Monaco editor, docs, metadata, etc. all integrate with the same store.
- [ ] Node Inspector
- Create a React-based Inspector for Triggers, Jobs, and Edges, moving the form logic and validations to the client.
- Provide a path for concurrency checks or CRDT integration.
👋 AI Disclaimer, this has been adjusted and proof-read - but portions of the above were assembled using AI.
- DataclipViewer & JobEditor now migrated to the new react implementation provided by @zeorin
- Updated the implementation of how React is mounted in
with-props.tsxto make sure that the mounted component has access tohandleEvent,pushEvent&pushEventTo. WithpushEventTothere's no need to pass the element node. It's already packaged as a closure before sent to the component. Hence, you need to just call it like apushEventand it'll attach the element itself.pushEventTo.bind(this, el)is how it's packaged with it's first argument. Hence you provide the rest. - Made store a singleton. Hence, can be initialized in the workflowEditor and then used via a hook in the JobEditor without the need to search for it.
The current existing bottleneck in the store has to do with the processing of pending changes in workflowEditor mount. It forces us to initialize the store inside workflowEditor.
https://github.com/OpenFn/lightning/pull/3043
@stuartc , @theroinaochieng , I spoke with the guys last week and it doesn't seem like this is in progress anymore (or at least not in "active" progress.) I'd like to see this at the very top of the tech backlog, and it still feels like lots of this part of the app are too hard or too slow. adding to R11
Got it @taylordowns2000