You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
DataExplorerPlugin and DiscoverPlugin are both independent plugins that are meant to work together. The DataExplorerPlugin is designed to handle the rendering of the application, and it depends on some services from DiscoverPlugin. For example, to render table in createCanvas from renderApp in DataExplorerPlugin, we need DiscoverServices to be built and initialized.
Currently the createCanvas function is indeed being called only once when the Discover plugin is being set up. The return value of this function (a React Element) is then saved in the ui object of the registered view and this same React Element is used every time the DataExplorerApp is rendered. Because React Elements are plain objects and not functions, they don't get re-evaluated or re-rendered unless their props or state change, which isn't the case here. What we want is to conditionally-render/re-render createCanvas based on the initialization of Discover services. However, these services within the DiscoverPlugin are initialized asynchronously, meaning they may not be ready when the DataExplorerPlugin mount.
The problem is how to get the createCanvas function to re-render once the services in DiscoverPlugin have been initialized, as merely updating the services with setServices(services) is insufficient to trigger a re-render, because services aren't being tracked as a state or prop within a React component.
Approach 1 The Observable Method and Its Challenges
The Observable approach involves DiscoverPlugin exposing an Observable that emits values reflecting the initialization state of the services. The DataExplorerPlugin would then subscribe to this Observable and conditionally render content based on the emitted values.
I have attempted to implement this by making DiscoverPlugin expose an Observable servicesInitialized$ as part of its start contract. DataExplorerPlugin then subscribes to this Observable and includes it in the services passed to createCanvas through OpenSearchDashboardsContextProvider. Here is some implementation details:
when render createCanvas, it will call useOpenSearchDashboards() to fetch services and discoverServicesInitialized$ will trigger setDiscoverServicesInitialized to update discoverServicesInitialized and will render new service
However, this method proved to be complex and non-functional for several reasons:
There's a challenge in passing the DiscoverStart contract to the mount function of the DataExplorerPlugin. OpenSearch Dashboards doesn't allow passing additional dependencies to the mount function directly.
DataExplorerPlugin should setup and start before DiscoverPlugin, meaning DiscoverPlugin's start contract might not be available when DataExplorerPlugin's mount method is called.
The Observable servicesInitialized$ was not triggering a re-render of the createCanvas function as expected, possibly because the Observable itself was not triggering a change in the React component state or props.
Approach 2 Using Shared State (Redux)
Some rough thoughts:
DataExplorerPlugin sets up a Redux store during its setup method. The store would hold the state of whether the services are initialized or not. Initially, this state would be false.
The DiscoverPlugin then receives a function to dispatch a servicesInitialized action (and a selector to read the state as part of its setup or start contract.
When the services in DiscoverPlugin finish initializing, it would dispatch the servicesInitialized action, setting the shared state to true.
The createCanvas function can then be connected to this shared state. When the state changes, it would trigger a re-render.
Option 3 Lazy load (selected)
Use React.lazy to load a component in createCanvas. In this case, React will render the fallback component specified inside Suspense. Once the import (or other async operation) completes and the component becomes available, React will render the actual component in place of the fallback.
The text was updated successfully, but these errors were encountered:
Description
DataExplorerPlugin and DiscoverPlugin are both independent plugins that are meant to work together. The DataExplorerPlugin is designed to handle the rendering of the application, and it depends on some services from DiscoverPlugin. For example, to render table in createCanvas from renderApp in DataExplorerPlugin, we need DiscoverServices to be built and initialized.
Currently the createCanvas function is indeed being called only once when the Discover plugin is being set up. The return value of this function (a React Element) is then saved in the ui object of the registered view and this same React Element is used every time the DataExplorerApp is rendered. Because React Elements are plain objects and not functions, they don't get re-evaluated or re-rendered unless their props or state change, which isn't the case here. What we want is to conditionally-render/re-render createCanvas based on the initialization of Discover services. However, these services within the DiscoverPlugin are initialized asynchronously, meaning they may not be ready when the DataExplorerPlugin mount.
The problem is how to get the createCanvas function to re-render once the services in DiscoverPlugin have been initialized, as merely updating the services with setServices(services) is insufficient to trigger a re-render, because services aren't being tracked as a state or prop within a React component.
Approach 1 The Observable Method and Its Challenges
The Observable approach involves DiscoverPlugin exposing an Observable that emits values reflecting the initialization state of the services. The DataExplorerPlugin would then subscribe to this Observable and conditionally render content based on the emitted values.
I have attempted to implement this by making DiscoverPlugin expose an Observable
servicesInitialized$
as part of its start contract. DataExplorerPlugin then subscribes to this Observable and includes it in the services passed to createCanvas through OpenSearchDashboardsContextProvider. Here is some implementation details:servicesInitialized
useOpenSearchDashboards()
to fetch services and discoverServicesInitialized$ will trigger setDiscoverServicesInitialized to update discoverServicesInitialized and will render new serviceHowever, this method proved to be complex and non-functional for several reasons:
The Observable servicesInitialized$ was not triggering a re-render of the createCanvas function as expected, possibly because the Observable itself was not triggering a change in the React component state or props.
Approach 2 Using Shared State (Redux)
Some rough thoughts:
DataExplorerPlugin sets up a Redux store during its setup method. The store would hold the state of whether the services are initialized or not. Initially, this state would be false.
The DiscoverPlugin then receives a function to dispatch a
servicesInitialized
action (and a selector to read the state as part of its setup or start contract.When the services in DiscoverPlugin finish initializing, it would dispatch the
servicesInitialized
action, setting the shared state to true.The createCanvas function can then be connected to this shared state. When the state changes, it would trigger a re-render.
Option 3 Lazy load (selected)
Use React.lazy to load a component in createCanvas. In this case, React will render the fallback component specified inside Suspense. Once the import (or other async operation) completes and the component becomes available, React will render the actual component in place of the fallback.
The text was updated successfully, but these errors were encountered: