Skip to content

Instantly share code, notes, and snippets.

@dpr-dev
Created May 13, 2019 15:08
Show Gist options
  • Select an option

  • Save dpr-dev/0bb7d91dd8b834d0843c24429d953ac6 to your computer and use it in GitHub Desktop.

Select an option

Save dpr-dev/0bb7d91dd8b834d0843c24429d953ac6 to your computer and use it in GitHub Desktop.

Revisions

  1. dpr-dev created this gist May 13, 2019.
    113 changes: 113 additions & 0 deletions hoc.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,113 @@
    import * as React from 'react';
    import { push, replace } from './router';

    export type RouteValidationResult = {
    url: string,
    needRedirect: boolean,
    state?: any,
    replace?: boolean
    };

    export type Options = {
    fn: (url: string) => Promise<RouteValidationResult>,
    onValidationRequest?: Function,
    onValidationFailure?: Function,
    placeholder?: () => React.ReactNode,
    errorPlaceholder?: () => React.ReactNode,
    skipeValidatonOnUpdate: boolean
    };

    type ValidationState = 'request' | 'complated' | 'failed';

    export const addValidator =
    (Component: React.ComponentType, options: Options) => {

    type Props = { url: string };
    type State = { validationState: ValidationState };

    return class extends React.Component<Props, State> {
    constructor(props: any) {
    super(props);
    this.state = { validationState: 'request' };
    }

    public componentDidMount = () => {
    this._validateRequest(false);
    }

    public componentDidUpdate = (props) => {
    if (options.skipeValidatonOnUpdate) {
    return;
    }

    if (this.props.url !== props.url) {
    this._validateRequest(true);
    }
    }

    public render = () => {
    const { validationState: state } = this.state;
    const { placeholder, errorPlaceholder } = options;

    if (state === 'request') {
    return !!placeholder ? placeholder() : null;
    }

    if (state === 'complated') {
    return <Component {...this.props} />;
    }

    return !!errorPlaceholder ? errorPlaceholder() : null;
    }


    private _validateRequest = async (force: boolean) => {
    const isNormalized =
    this.props.url.includes('normalized');

    if (!isNormalized || force) {
    this.setState({ validationState: 'request' });
    const {
    onValidationRequest,
    onValidationFailure
    } = options;

    try {
    if (!!onValidationRequest) {
    await onValidationRequest();
    }

    await this._executeValidation();
    } catch (e) {
    if (!!onValidationFailure) {
    await onValidationFailure(e);
    }

    return this.setState({ validationState: 'failed' });
    }
    }

    this.setState({ validationState: 'complated' });
    }

    private _executeValidation = async () => {
    const {
    needRedirect,
    replace: r,
    url,
    state
    } = await options.fn(this.props.url);

    if (needRedirect) {
    const action = r ? replace : push;
    const u =
    url.includes('?')
    ? `${url}&normalized=true`
    : `${url}?normalized=true`;

    action(u, state);
    }
    }
    }
    };