<commit_before>import React, { Component, PureComponent } from 'react';
import ReactDOM from 'react-dom';
import isBrowser from 'utils/isBrowser';
import defer from 'lodash/defer';

import PropTypes from 'prop-types';

import Loading from './Loading';

// Global loading instance
let loadingInstance;

export default class Instance extends (PureComponent || Component) {
  static propTypes = {
    prefix: PropTypes.string,
    className: PropTypes.string,
    float: PropTypes.bool,
    show: PropTypes.bool,
    zIndex: PropTypes.number,
    containerClass: PropTypes.string,
  };

  static defaultProps = {
    prefix: 'zent',
    className: '',
    // FIXME: use defaultProps when we drop support for static
    // float: false,
    show: false,
    zIndex: 9998,
    containerClass: '',
  };

  componentDidMount() {
    this.renderLoading();
  }

  // 因为需要确保内部元素已经 render 完毕，所以使用 did，但是因此带来额外的对 show 字段的判断逻辑
  componentDidUpdate() {
    this.renderLoading();
  }

  componentWillUnmount() {
    if (this.instance) {
      let loadingContainer = this.instance.container;
      ReactDOM.unmountComponentAtNode(this.instance.container);
      loadingContainer.parentNode.removeChild(loadingContainer);
    }
  }

  // 通过以下函数与组件通信
  renderLoading(target) {
    // static 方式，loading 将存在于文档流中
    if (!isFloat(this.props)) {
      return;
    }

    if (!this.instance) {
      if (!target) {
        target = ReactDOM.findDOMNode(this);
      }
      this.instance = newInstance({
        ...this.props,
        target,
      });
    } else if (this.instance.show) {
      this.instance.show(this.props);
    }
  }

  render() {
    const float = isFloat(this.props);

    if (!isFloat(this.props)) {
      return (
        <Loading {...this.props} float={float}>
          {this.props.children}
        </Loading>
      );
    }

    // Return null to make React happy if Loading has no children
    return this.props.children || null;
  }
}

// ReactDOM.render returns null when called inside lifecycle methods
// Just a workaround
// These methods should be considered deprecated, don't use them.
Instance.on = options => defer(on, options);
Instance.off = options => defer(off, options);
Instance.newInstance = props => defer(newInstance, props);

function on({
  prefix = 'zent',
  className = '',
  containerClass = '',
  zIndex = 9998,
} = {}) {
  if (!isBrowser) return;

  if (!loadingInstance) {
    loadingInstance = newInstance({
      show: true,
      prefix,
      className,
      containerClass,
      zIndex,
      float: true,
    });

    return;
  }

  loadingInstance.show({
    show: true,
  });
}

function off() {
  if (!isBrowser) return;

  if (!loadingInstance) return;

  loadingInstance.show({
    show: false,
  });
}

function newInstance(props) {
  if (!isBrowser) return;

  let div = document.createElement('div');
  div.className = `${props.prefix}-loading-container ${props.containerClass}`;
  document.body.appendChild(div);

  // FIXME: loading may be null in React 16
  let loading = ReactDOM.render(<Loading {...props} />, div);
  return {
    show: loading && loading.show,
    container: div,
  };
}

// FIXME: remove support for props.static
function isFloat(props) {
  const hasStatic = props.hasOwnProperty('static');
  const hasFloat = props.hasOwnProperty('float');

  if (hasFloat) {
    return props.float;
  }

  return hasStatic ? !props.static : false;
}<commit_msg>bugfix: fix loading cannot be close because ReactDOM.render return null (#833)<commit_after>import React, { Component, PureComponent } from 'react';
import ReactDOM from 'react-dom';
import isBrowser from 'utils/isBrowser';

import PropTypes from 'prop-types';

import Loading from './Loading';

// Global loading instance
let loadingInstance;

export default class Instance extends (PureComponent || Component) {
  static propTypes = {
    prefix: PropTypes.string,
    className: PropTypes.string,
    float: PropTypes.bool,
    show: PropTypes.bool,
    zIndex: PropTypes.number,
    containerClass: PropTypes.string,
  };

  static defaultProps = {
    prefix: 'zent',
    className: '',
    // FIXME: use defaultProps when we drop support for static
    // float: false,
    show: false,
    zIndex: 9998,
    containerClass: '',
  };

  componentDidMount() {
    this.renderLoading();
  }

  // 因为需要确保内部元素已经 render 完毕，所以使用 did，但是因此带来额外的对 show 字段的判断逻辑
  componentDidUpdate() {
    this.renderLoading();
  }

  componentWillUnmount() {
    if (this.instance) {
      this.instance.then(({ container }) => {
        ReactDOM.unmountComponentAtNode(container);
        container.parentNode.removeChild(container);
      });
    }
  }

  // 通过以下函数与组件通信
  renderLoading(target) {
    // static 方式，loading 将存在于文档流中
    if (!isFloat(this.props)) {
      return;
    }

    if (!this.instance) {
      if (!target) {
        target = ReactDOM.findDOMNode(this);
      }
      this.instance = newInstance({
        ...this.props,
        target,
      });
    } else if (this.instance) {
      this.instance.then(({ show }) => {
        show && show(this.props);
      });
    }
  }

  render() {
    const float = isFloat(this.props);

    if (!isFloat(this.props)) {
      return (
        <Loading {...this.props} float={float}>
          {this.props.children}
        </Loading>
      );
    }

    // Return null to make React happy if Loading has no children
    return this.props.children || null;
  }
}

// Just a workaround
// These methods should be considered deprecated, don't use them.
Instance.on = on;
Instance.off = off;
Instance.newInstance = newInstance;

function on({
  prefix = 'zent',
  className = '',
  containerClass = '',
  zIndex = 9998,
} = {}) {
  if (!isBrowser) return;

  if (!loadingInstance) {
    loadingInstance = newInstance({
      show: true,
      prefix,
      className,
      containerClass,
      zIndex,
      float: true,
    });

    return;
  }

  loadingInstance.then(({ show }) => {
    show && show({ show: true });
  });
}

function off() {
  if (!isBrowser) return;

  if (!loadingInstance) return;

  loadingInstance.then(({ show }) => {
    show && show({ show: false });
  });
}

function newInstance(props) {
  if (!isBrowser) return;

  let div = document.createElement('div');
  div.className = `${props.prefix}-loading-container ${props.containerClass}`;
  document.body.appendChild(div);

  return new Promise(resolve => {
    ReactDOM.render(
      <Loading
        {...props}
        ref={loading => {
          if (loading) {
            resolve({
              show: loading && loading.show,
              container: div,
            });
          }
        }}
      />,
      div
    );
  });
}

// FIXME: remove support for props.static
function isFloat(props) {
  const hasStatic = props.hasOwnProperty('static');
  const hasFloat = props.hasOwnProperty('float');

  if (hasFloat) {
    return props.float;
  }

  return hasStatic ? !props.static : false;
}
{'fraction_non_alphanumeric': 0.13312202852614896, 'fraction_numerical': 0.0030255006483215674, 'mean_word_length': 2.7242489270386265, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 18, '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': '1074842', 'n_tokens_mistral': 2177, 'n_tokens_neox': 2157, 'n_words': 520}