Closed JeanLucPons closed 3 years ago
Here is how i implemented the onPreload callback. I used only Fields and the hook I defined in this post It works well and may be adapted to model-react.
import { FC, ReactNode } from "react";
import { DataSource } from "./DataSource";
import { Field } from "./Field";
import { useData } from "./useData";
/**
* A component to handle the loading or error state of loadable data sources
*/
export const AppLoader: FC<{
/** The Loadable */
source: AppDataLoader;
/** The content to show when there are no exceptions and data loaded */
whenLoaded: ReactNode | ((loadingInfo: any) => ReactNode);
/** The content to show before loading the data, when the component set
* the loadingInfo , the AppLoader switch to onLoading if defined, whenLoaded otherwise */
onPreLoad?: ReactNode | ((loadingInfo: DataSource) => ReactNode);
/** The node to show while loading */
onLoading?: ReactNode | ((progress: any, loadingInfo: any) => ReactNode);
/** The node to show if an error occurred during loading, it has to be defined if a loader is used */
onError?: ReactNode | ((exception: Error) => ReactNode);
}> = ({ source, whenLoaded ,onPreLoad, onLoading, onError }) => {
// -------------------------------------------------------
// Internal function
// -------------------------------------------------------
function preloading() {
return <>{onPreLoad instanceof Function ?
onPreLoad(source.LoadingInfo()) :
onPreLoad}</>;
}
function loading() {
source.load();
return <>{onLoading instanceof Function ?
onLoading(source.Progress().get(), source.LoadingInfo().get()) :
onLoading}</>;
}
function loaded() {
return <>{whenLoaded instanceof Function ? whenLoaded(source.LoadingInfo().get()) : whenLoaded}</>;
}
function error() {
return <>{onError instanceof Function ? onError(source.Error().get()) : onError}</>;
}
// -------------------------------------------------------
// Listen to data
// -------------------------------------------------------
let event = useData([source.LoadingInfo(), source.Progress(), source.Loaded(), source.Error()]);
if (event === undefined) {
if (onPreLoad) {
return preloading();
} else if (onLoading) {
return loading();
} else {
return loaded();
}
} else if (event === source.LoadingInfo()) {
if (onLoading) {
return loading();
} else {
return loaded();
}
} else if (event == source.Progress()) {
return loading();
} else if (event === source.Error()) {
return error();
} else { // event === source.Loaded()
return loaded();
}
};
/**
* Application loader
*/
export class AppDataLoader {
protected loadingInfo: Field<any>; // Loading info coming from preLoad
protected progress: Field<any>; // Progress field updated by the loader
protected error: Field<Error>; // Exception thrown by the the loader
protected loaded: Field<string>; // Result of the loader promise
protected isLoading: boolean;
protected loader: (loader: AppDataLoader) => Promise<string>;
constructor(
loader: (loader: AppDataLoader) => Promise<string>,
initialStatus: string,
initialProgress: any = {}
) {
this.loader = loader;
this.isLoading = false;
this.loadingInfo = new Field({});
this.progress = new Field(initialProgress);
this.loaded = new Field(initialStatus);
this.error = new Field(new Error(""));
}
/**
* Containing loading inforamtion, it must be set by the onPreload callback of the
* AppLoader component (if used).
*/
LoadingInfo() {
return this.loadingInfo;
}
/**
* Sets the progress value.
* It can be set only from the loader function
*/
Progress() {
return this.progress;
}
/**
* Signals that the loader ends its execution
*/
Loaded() {
return this.loaded;
}
/**
* Signals that the loader ends its execution with an exception
*/
Error() {
return this.error;
}
/**
* Launch the loader function
*/
async load(): Promise<void> {
if (!this.isLoading) {
this.isLoading = true;
try {
let s = await this.loader(this);
this.loaded.set(s);
} catch (e) {
this.error.set(e);
}
}
}
}
Interesting idea. I think having a loader source with progress tracking could be useful for Model-react, but it wouldn't be in this form. As we discussed in the other thread, this approach doesn't really support all features I'm after.
Maybe you could make your own library for your version of the system :) I think there will probably be interest in your simplified (and more performant) system. I just really need the flexibility of mine myself.
"My" lib will integrate a set of advanced Material UI/React components to build application for a control system (SCADA) and will be submitted to code reviews. The MVC kernel will be just 3 files (abstract datasoure class, Field class and useData hook) and I need to prove that it is safe, that it will not miss callbacks and that it can handle thousands of listeners with asynchronous refreshers that poll data and also events coming from outside through WebSocket. I don't think I will made a small lib of 3 files for the MVC kernel. i let you inform when I'll commit something it but still heavy work to perform. I'm just starting the global skeleton and it needs to be strong enough...
Hello,
I'm progressing well with your lib. No particular issue found :) I would like to add a onPreLoad callback on the dataloader I made. For instance to handle a login panel before loading the data. I think I can do it by adding an other Field to my AppDataLoader but is there a more efficient way to do that ? Many thanks.