withRenderCtrl.tsx
import React from 'react';
import Loading from '../../commons/loading/loading';
import ErrorBoundary from '../../commons/error/errorBoundary';
import EmptyHint from '../../commons/empty/emptyHint';
export interface WithRenderCtrlProp {
isDataReady: boolean;
isLoading: boolean;
customEmptyMessage?: string | null;
errorMessage: string | null;
}
const withRenderCtrl = <P extends object>(Component: React.ComponentType<P>) =>
class WithLoading extends React.Component<P & WithRenderCtrlProp> {
render() {
const { isDataReady, isLoading, errorMessage, customEmptyMessage, ...props } = this.props;
if (isDataReady) return <Component {...props as P} />;
if (errorMessage) return <ErrorBoundary errorMessage={errorMessage}/>;
if (isLoading) return <Loading />;
return <EmptyHint customMessage={customEmptyMessage}/>;
}
}
export default withRenderCtrl;
emptyHint.tsx
import React from 'react';
interface IProps {
customMessage?: string | null;
}
// custom message is not necessary, if it is not given then a common message will be shown
const EmptyHint = ({ customMessage = null }: IProps) => (
<div>
<div>{customMessage ? customMessage : 'common message for no data found '}</div>
</div>
);
export default EmptyHint;
errorBoundary.tsx
import React from 'react';
interface IState {
error: any;
info: any;
}
interface IProps {
errorMessage: any | null;
}
// also it has componentDidCatch method
class ErrorBoundary extends React.Component<IProps, IState> {
constructor(props) {
super(props);
this.state = {
error: null,
info: null,
};
}
componentDidCatch(error, info) {
this.setState({
error: error,
info: info,
});
}
render() {
if (this.state.error || this.props.errorMessage) {
return (
<div>
<h1>...... :( </h1>
{this.state.error && this.state.error.toString()}
{this.props.errorMessage && this.props.errorMessage.toString()}
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
loading.tsx
import React from 'react';
const Loading = () => (
<div>Wait for loading....</div>
);
export default Loading;
example:
import React from 'react';
// define props, states vs. ...
class Example extends React.PureComponent {
// ...
// when you calling your api, you should set loading, error props.
async componentDidMount() {
const { setMyData, setLoader, setError } = this.props;
setLoader(true);
const myData = await getMyData()
.catch((error) => {
setError(error);
});
setLoader(false);
setMyData(myData);
}
render(){
const { myData, isLoading, error } = this.props;
// usually your main component gets data, sets error & loading cases. It has callback methods vs.
// The component named with Body includes html, css stuffs.
// and you should define your body component as following. (with wraping our HOC named as withRenderCtrl)
// export default withRenderCtrl(ExampleBody);
return(
<ExampleBody
myData={myData}
isLoading={isLoading}
errorMessage={error}
isDataReady={myData && !myData.isEmpty()}
customEmptyMessage="my custom empty data >> this props is not necessary"
/>
);
}
}
export default Example;
References: https://github.com/kenneth1003/react-render-ctrl
No comments:
Post a Comment