<commit_before>import React from 'react';
import {shouldComponentUpdate} from 'react/lib/ReactComponentWithPureRenderMixin';
import {connect} from 'react-redux';
import {ATTACKS_REMOVE} from './attacks/reducer';
import {differ} from './lib/differ';


const RemoverContent = React.createClass({
  propTypes: {
    attacks: React.PropTypes.object.isRequired,
    onRemove: React.PropTypes.func.isRequired
  },


  componentWillMount() {
    this.timers = {};
    this.diff = differ(this.timers);
  },


  componentDidMount() {
    const {attacks} = this.props;
    const {add} = this.diff(attacks);
    add.forEach(this.setTimeout);
  },


  componentWillReceiveProps({attacks}) {
    const {add} = this.diff(attacks);
    add.forEach(this.setTimeout);
  },


  shouldComponentUpdate,


  componentWillUnmount() {
    Object.keys(this.timers)
      .forEach(id => clearTimeout(this.timers[id]));
  },


  setTimeout(id) {
    clearTimeout(this.timers[id]);
    this.timers[id] = setTimeout(() => this.onRemove(id), 3000);
  },


  onRemove(id) {
    const {onRemove} = this.props;
    clearTimeout(this.timers[id]);
    delete this.timers[id];
    onRemove(id);
  },


  render() {
    return false;
  }
});


const mapStateToProps = ({
  attacks
}) => ({
  attacks
});


const mapDispatchToProps = dispatch => ({
  onRemove: id => dispatch({type: ATTACKS_REMOVE, ids: [id]})
});


export const Remover = connect(
  mapStateToProps,
  mapDispatchToProps
)(RemoverContent);
<commit_msg>Add additional hard limiter for attacks<commit_after>import React from 'react';
import {shouldComponentUpdate} from 'react/lib/ReactComponentWithPureRenderMixin';
import {connect} from 'react-redux';
import {ATTACKS_REMOVE} from './attacks/reducer';
import {differ} from './lib/differ';


const RemoverContent = React.createClass({
  propTypes: {
    attacks: React.PropTypes.object.isRequired,
    onRemove: React.PropTypes.func.isRequired
  },


  componentWillMount() {
    this.timers = {};
    this.diff = differ(this.timers);
  },


  componentDidMount() {
    const {attacks} = this.props;
    const {add} = this.diff(attacks);
    add.forEach(this.setTimeout);
  },


  componentWillReceiveProps({attacks}) {
    const {add} = this.diff(attacks);
    add.forEach(this.setTimeout);

    // safeguard, no more then 20 attacks at once
    const MAX = 20;
    const ids = Object.keys(attacks);
    ids.slice(0, ids.length - Math.min(MAX, ids.length)).forEach(this.onRemove);
  },


  shouldComponentUpdate,


  componentWillUnmount() {
    Object.keys(this.timers)
      .forEach(id => clearTimeout(this.timers[id]));
  },


  setTimeout(id) {
    clearTimeout(this.timers[id]);
    this.timers[id] = setTimeout(() => this.onRemove(id), 3000);
  },


  onRemove(id) {
    const {onRemove} = this.props;
    clearTimeout(this.timers[id]);
    delete this.timers[id];
    onRemove(id);
  },


  render() {
    return false;
  }
});


const mapStateToProps = ({
  attacks
}) => ({
  attacks
});


const mapDispatchToProps = dispatch => ({
  onRemove: id => dispatch({type: ATTACKS_REMOVE, ids: [id]})
});


export const Remover = connect(
  mapStateToProps,
  mapDispatchToProps
)(RemoverContent);

{'fraction_non_alphanumeric': 0.15021998742928974, 'fraction_numerical': 0.004085480829666876, 'mean_word_length': 3.786466165413534, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, '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': '5781580', 'n_tokens_mistral': 1102, 'n_tokens_neox': 1054, 'n_words': 197}