<commit_before>import React, { PropTypes } from 'react';
import { compose } from 'redux';
import { contain } from 'redux-epic';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import PureComponent from 'react-pure-render/component';

import Classic from './classic/Classic.jsx';
import Step from './step/Step.jsx';
import Project from './project/Project.jsx';
import Video from './video/Video.jsx';

import {
  fetchChallenge,
  fetchChallenges,
  replaceChallenge
} from '../redux/actions';
import { challengeSelector } from '../redux/selectors';

const views = {
  step: Step,
  classic: Classic,
  project: Project,
  simple: Project,
  video: Video
};

const bindableActions = {
  fetchChallenge,
  fetchChallenges,
  replaceChallenge
};

const mapStateToProps = createSelector(
  challengeSelector,
  state => state.challengesApp.challenge,
    ({ viewType }, challenge) => ({
    challenge,
    viewType
  })
);

const fetchOptions = {
  fetchAction: 'fetchChallenge',
  getActionArgs({ params: { block, dashedName } }) {
    return [ dashedName, block ];
  },
  isPrimed({ challenge }) {
    return !!challenge;
  }
};

export class Challenges extends PureComponent {
  static displayName = 'Challenges';

  static propTypes = {
    isStep: PropTypes.bool,
    fetchChallenges: PropTypes.func,
    replaceChallenge: PropTypes.func,
    params: PropTypes.object
  };

  componentDidMount() {
    this.props.fetchChallenges();
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.params.dashedName !== nextProps.params.dashedName) {
      this.props.replaceChallenge({
        dashedName: nextProps.params.dashedName,
        block: nextProps.params.block
      });
    }
  }

  renderView(viewType) {
    const View = views[viewType] || Classic;
    return <View />;
  }

  render() {
    const { viewType } = this.props;
    return (
      <div>
        { this.renderView(viewType) }
      </div>
    );
  }
}

export default compose(
  connect(mapStateToProps, bindableActions),
  contain(fetchOptions)
)(Challenges);
<commit_msg>Fix(map): prevent loading a challenge from refetching<commit_after>import React, { PropTypes } from 'react';
import { compose } from 'redux';
import { contain } from 'redux-epic';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import PureComponent from 'react-pure-render/component';

import Classic from './classic/Classic.jsx';
import Step from './step/Step.jsx';
import Project from './project/Project.jsx';
import Video from './video/Video.jsx';

import {
  fetchChallenge,
  fetchChallenges,
  replaceChallenge
} from '../redux/actions';
import { challengeSelector } from '../redux/selectors';

const views = {
  step: Step,
  classic: Classic,
  project: Project,
  simple: Project,
  video: Video
};

const bindableActions = {
  fetchChallenge,
  fetchChallenges,
  replaceChallenge
};

const mapStateToProps = createSelector(
  challengeSelector,
  state => state.challengesApp.challenge,
  state => state.challengesApp.superBlocks,
    ({ viewType }, challenge, superBlocks = []) => ({
    challenge,
    viewType,
    areChallengesLoaded: superBlocks.length > 0
  })
);

const fetchOptions = {
  fetchAction: 'fetchChallenge',
  getActionArgs({ params: { block, dashedName } }) {
    return [ dashedName, block ];
  },
  isPrimed({ challenge }) {
    return !!challenge;
  }
};

export class Challenges extends PureComponent {
  static displayName = 'Challenges';

  static propTypes = {
    isStep: PropTypes.bool,
    fetchChallenges: PropTypes.func,
    replaceChallenge: PropTypes.func,
    params: PropTypes.object,
    areChallengesLoaded: PropTypes.bool
  };

  componentDidMount() {
    if (!this.props.areChallengesLoaded) {
      this.props.fetchChallenges();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.params.dashedName !== nextProps.params.dashedName) {
      this.props.replaceChallenge({
        dashedName: nextProps.params.dashedName,
        block: nextProps.params.block
      });
    }
  }

  renderView(viewType) {
    const View = views[viewType] || Classic;
    return <View />;
  }

  render() {
    const { viewType } = this.props;
    return (
      <div>
        { this.renderView(viewType) }
      </div>
    );
  }
}

export default compose(
  connect(mapStateToProps, bindableActions),
  contain(fetchOptions)
)(Challenges);

{'fraction_non_alphanumeric': 0.1260485150759465, 'fraction_numerical': 0.00022670596236681024, 'mean_word_length': 3.572020725388601, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, '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': '4732708', 'n_tokens_mistral': 1419, 'n_tokens_neox': 1361, 'n_words': 304}