<commit_before>import * as React from "react";
import * as PropTypes from "prop-types";

import { Model, ModelError } from "../Model";
import { FormProps, FormPropTypes } from "./FormProps";
import { FormContext, FormContextTypes } from "./FormContext";

export interface FormState<M> {
    model: M,
    mounted: {
        [key: string]: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,
    },
    isLoading: boolean,
}

export class Form<M extends Model>
    extends React.Component<React.HTMLProps<HTMLFormElement> & FormProps<M>, FormState<M>> {

    public static propTypes = FormPropTypes;
    public static childContextTypes = FormContextTypes;

    public state: FormState<M>;

    constructor(props: FormProps<M>) {
        super(props as any);

        this.state = {
            model: this.props.instantiate(),
            mounted: {},
            isLoading: false,
        };
    }

    public getChildContext(): FormContext {
        return {
            onChange: this.handleChange,
            values: this.state.model.values,

            onMount: this.handleMount,
            onUnmount: this.handleUnmount,

            validate: this.validate,
            getDOMElement: this.getDOMElement,

            isLoading: this.state.isLoading,
            addError: this.handleErrorAdded,
            getError: this.state.model.getError,
        };
    }

    public async componentWillMount() {
        await this.state.model.get();

        if (this.props.storageKey) {
            this.loadFromStorage();
        }
    }

    public async componentWillUnmount() {
        if (this.props.storageKey) {
            this.pushToStorage();
        }
    }

    public handleSubmit = async (event?: Event): Promise<void> => {
        event && event.preventDefault();

        this.state.isLoading = true;
        this.forceUpdate();

        await this.state.model.validate();

        if (!this.state.model.hasErrors()) {
            const action = this.state.model[this.props.method];
            if ("function" === typeof action) {
                await action();
            } else {
                this.props.onSubmit && await this.props.onSubmit(this.state.model, this.getChildContext());
            }
        } else {
            this.getDOMElement(this.state.model.getErrors()[0].attribute).focus();
        }

        this.state.isLoading = false;
        this.forceUpdate();
    };

    public render(): JSX.Element {
        const { instantiate, onSubmit, method, storageKey, ...childProps } = this.props;

        return (
            <form onSubmit={this.handleSubmit as any} {...childProps}>
                {this.props.children}
            </form>
        );
    }

    public getDOMElement = (attribute: string): HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement => {
        return this.state.mounted[attribute];
    };

    public loadFromStorage(): boolean {
        let localStorageValue;
        try {
            localStorageValue = JSON.parse(window.localStorage.getItem(this.props.storageKey));
        } catch (exception) {
            return false;
        }

        if (localStorageValue) {
            this.state.model.attributes()
                .filter((attribute: string) => localStorageValue.hasOwnProperty(attribute))
                .forEach(
                (attribute: string) => this.handleChange(attribute, localStorageValue[attribute])
                );
            return true;
        }

        return false;
    }

    public pushToStorage(): void {
        const localStorageValue = {};
        this.state.model.attributes()
            .filter((attribute: string) => this.state.model[attribute] !== undefined)
            .forEach((attribute: string) => localStorageValue[attribute] = this.state.model[attribute]);

        window.localStorage
            && window.localStorage.setItem(this.props.storageKey, JSON.stringify(localStorageValue));
    }

    protected handleChange = (attribute: string, value: any): void => {
        if (this.state.model[attribute] === value) {
            return;
        }
        this.state.model[attribute] = value;
        this.forceUpdate();
    };

    protected handleMount =
        (attribute: string, element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement) => {
            if (this.state.mounted[attribute] === element) {
                return;
            }
            this.state.mounted[attribute] = element;
            this.forceUpdate();
        };

    protected handleUnmount = (attribute: string): void => {
        if (this.state.mounted[attribute] === undefined) {
            return;
        }
        delete this.state.mounted[attribute];
        this.forceUpdate();
    };

    protected validate = async (group: string): Promise<ModelError[]> => {
        const errorsRemoved = (this.state.model.groups()[group] || [])
            .map((attribute: string) => this.state.model.removeErrors(attribute))
            .reduce((carry: number, attributeErrorsCount: number) => carry + attributeErrorsCount, 0);

        const errors = await this.state.model.validate(group);
        if (errors.length > 0 || errorsRemoved > 0) {
            this.forceUpdate();
        }

        return errors;
    };

    protected handleErrorAdded = (newError: ModelError): void => {
        this.state.model.addError(newError);
        this.forceUpdate();
    }
}
<commit_msg>Load data from cache first<commit_after>import * as React from "react";
import * as PropTypes from "prop-types";

import { Model, ModelError } from "../Model";
import { FormProps, FormPropTypes } from "./FormProps";
import { FormContext, FormContextTypes } from "./FormContext";

export interface FormState<M> {
    model: M,
    mounted: {
        [key: string]: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,
    },
    isLoading: boolean,
}

export class Form<M extends Model>
    extends React.Component<React.HTMLProps<HTMLFormElement> & FormProps<M>, FormState<M>> {

    public static propTypes = FormPropTypes;
    public static childContextTypes = FormContextTypes;

    public state: FormState<M>;

    constructor(props: FormProps<M>) {
        super(props as any);

        this.state = {
            model: this.props.instantiate(),
            mounted: {},
            isLoading: false,
        };
    }

    public getChildContext(): FormContext {
        return {
            onChange: this.handleChange,
            values: this.state.model.values,

            onMount: this.handleMount,
            onUnmount: this.handleUnmount,

            validate: this.validate,
            getDOMElement: this.getDOMElement,

            isLoading: this.state.isLoading,
            addError: this.handleErrorAdded,
            getError: this.state.model.getError,
        };
    }

    public async componentWillMount() {
        if (this.props.storageKey) {
            this.loadFromStorage();
        }

        await this.state.model.get();
    }

    public async componentWillUnmount() {
        if (this.props.storageKey) {
            this.pushToStorage();
        }
    }

    public handleSubmit = async (event?: Event): Promise<void> => {
        event && event.preventDefault();

        this.state.isLoading = true;
        this.forceUpdate();

        await this.state.model.validate();

        if (!this.state.model.hasErrors()) {
            const action = this.state.model[this.props.method];
            if ("function" === typeof action) {
                await action();
            } else {
                this.props.onSubmit && await this.props.onSubmit(this.state.model, this.getChildContext());
            }
        } else {
            this.getDOMElement(this.state.model.getErrors()[0].attribute).focus();
        }

        this.state.isLoading = false;
        this.forceUpdate();
    };

    public render(): JSX.Element {
        const { instantiate, onSubmit, method, storageKey, ...childProps } = this.props;

        return (
            <form onSubmit={this.handleSubmit as any} {...childProps}>
                {this.props.children}
            </form>
        );
    }

    public getDOMElement = (attribute: string): HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement => {
        return this.state.mounted[attribute];
    };

    public loadFromStorage(): boolean {
        let localStorageValue;
        try {
            localStorageValue = JSON.parse(window.localStorage.getItem(this.props.storageKey));
        } catch (exception) {
            return false;
        }

        if (localStorageValue) {
            this.state.model.attributes()
                .filter((attribute: string) => localStorageValue.hasOwnProperty(attribute))
                .forEach(
                (attribute: string) => this.handleChange(attribute, localStorageValue[attribute])
                );
            return true;
        }

        return false;
    }

    public pushToStorage(): void {
        const localStorageValue = {};
        this.state.model.attributes()
            .filter((attribute: string) => this.state.model[attribute] !== undefined)
            .forEach((attribute: string) => localStorageValue[attribute] = this.state.model[attribute]);

        window.localStorage
            && window.localStorage.setItem(this.props.storageKey, JSON.stringify(localStorageValue));
    }

    protected handleChange = (attribute: string, value: any): void => {
        if (this.state.model[attribute] === value) {
            return;
        }
        this.state.model[attribute] = value;
        this.forceUpdate();
    };

    protected handleMount =
        (attribute: string, element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement) => {
            if (this.state.mounted[attribute] === element) {
                return;
            }
            this.state.mounted[attribute] = element;
            this.forceUpdate();
        };

    protected handleUnmount = (attribute: string): void => {
        if (this.state.mounted[attribute] === undefined) {
            return;
        }
        delete this.state.mounted[attribute];
        this.forceUpdate();
    };

    protected validate = async (group: string): Promise<ModelError[]> => {
        const errorsRemoved = (this.state.model.groups()[group] || [])
            .map((attribute: string) => this.state.model.removeErrors(attribute))
            .reduce((carry: number, attributeErrorsCount: number) => carry + attributeErrorsCount, 0);

        const errors = await this.state.model.validate(group);
        if (errors.length > 0 || errorsRemoved > 0) {
            this.forceUpdate();
        }

        return errors;
    };

    protected handleErrorAdded = (newError: ModelError): void => {
        this.state.model.addError(newError);
        this.forceUpdate();
    }
}

{'fraction_non_alphanumeric': 0.12108906322104292, 'fraction_numerical': 0.0007383479464697738, 'mean_word_length': 2.1527494908350304, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '575691', 'n_tokens_mistral': 2893, 'n_tokens_neox': 2806, 'n_words': 582}