ReactJS - приложение работает в разных файлах

Я пытаюсь сделать основанную на React веб-игру. У меня есть компонент приложения, который содержит почти все не-UX состояние. Чтобы избежать дублирования кода, я также держу в нем большинство функций и передаю его как опору дочерним компонентам.

Но теперь я начинаю загромождаться различными функциями, все в теле приложения. Есть ли простой способ удовлетворительной структуры этого в разных файлах? Должен ли я уже заглянуть в государственные библиотеки управления?

В настоящее время материал выглядит так:

    class App extends Component {
  constructor(props) {
    super(props);
    this.state = gameInitialize();
    this.modifyState = this.modifyState.bind(this);
    this.moveUnit = this.moveUnit.bind(this);
    this.progressMission = this.progressMission.bind(this);
    this.timeJump = this.timeJump.bind(this);
    this.competenceAfterTimeJump = this.competenceAfterTimeJump.bind(this);
    this.save = this.save.bind(this);
    this.load = this.load.bind(this);
  }
  componentDidMount() {
    this.timerID = setInterval(this.modifyState, this.state.interval);
    window.addEventListener('beforeunload', this.save);
    this.load();
  }
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
  save() {
    localStorage.setItem("gameSave", toJson(this.state));
  }
  load() {
    let state = 0;
    try {
      state = fromJson(localStorage.getItem("gameSave"));
    } catch (error) {
      console.log(error);
      return 0;
    }
    state.units.map(unit => {
      delete unit.__parent;
      delete unit.attributes.__parent
      return 0;
    });
    state.missions.map(mission => delete mission.__parent);
    this.setState(state);
  }
  modifyState() {
    this.setState(this.state.units.map(this.progressMission));
    this.setState(this.state);
  }
  progressMission(unit) {
    const mission = unit.currentMission;
    let increment = unit.attributes[mission.type].total() - mission.complexity;
    if (increment < 0) increment = 0;
    mission.progress += increment * this.state.interval / 1000 * unit.competence / 10;
    if (mission.progress >= mission.difficulty) {
      mission.progress = 0;
      this.state.experience.get(mission.reward);
      mission.completions += 1;
    }
  }
  moveUnit(unit, mission) {
    unit.currentMission = mission;
    this.setState(this.state);
  }
  timeJump() {
    const game = this.state;
    while (game.units.length > 2) {
      game.units.pop();
    };
    game.units.map(function (unit) {
      Object.keys(unit.attributes).map((key) => { unit.attributes[key] = newAttribute() });
      unit.currentMission = game.missions[0];
    });
    game.missions.map((mission) => {mission.progress = 0});
    game.units[0].competence = this.competenceAfterTimeJump();
    game.experience.current = 0;
    this.setState(game);
  }
  competenceAfterTimeJump() {
    return (10 + Math.sqrt(this.state.experience.total) / 10);
  }

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <h1 className="title">Time-traveling Hero: eventually I'll save the world, or maybe not if I don't feel it</h1>
        </header>
        <SaveLoad game={this} />
        <Prestige game={this} />
        <MissionWrapper>
          <MissionList missions={this.state.missions} game={this} />
        </MissionWrapper>
        <UnitWrapper>
          <ExpWrapper>
            <div>
              Available Experience: {this.state.experience.current.toFixed(1)}
            </div>
            <div>
              Total Experience: {this.state.experience.total.toFixed(1)}
            </div>
          </ExpWrapper>
          <UnitList units={this.state.units} game={this} />
        </UnitWrapper>
      </div>
    );
  }
}

function gameInitialize() {
  let game = { units: [], missions: [], currentUnit: undefined };

  game.interval = 10;

  game.missions = generateMissions(50);

  game.experience = {
    current: 0, total: 0,
    get: function (amount) { this.current += amount; this.total += amount },
    spend: function (amount) {
      if (this.current >= amount) {
        this.current -= amount;
        return true;
      }
      else return false;
    }
  };

  game.units.push({ name: "Hero", attributes: newAttributes(), competence: 10, currentMission: game.missions[0] });
  game.units.push({ name: "Childhood Friend", attributes: newAttributes(), competence: 15, currentMission: game.missions[0] });

  game.currentUnit = game.units[0];

  game.missionsWithUnits = function () {
    this.missions.map()
  }
  return game;
}

Как мне поступить?

1 ответ

Да, это очень легко организовать код JS! Используйте модули. Вот как это сделать.

  1. Экспорт функций из файла

adders.js:

export function addTwo (number) {
  return number + 2
}
  1. Тогда используйте это:

Это может быть в файле компонента:

import { addTwo } from './path/to/adders.js'
console.log(addTwo(5)) // logs 7

Вы можете организовать это очень хорошо для многих вещей. Если у вас есть группа связанных функций, используйте такой модуль. вот структура файла:

mathStuff/
  adders.js
  index.js

У вас есть все связанные файлы в одной папке, а ваши функции экспортированы из отдельных файлов, как указано выше. Затем настройте индекс так:

index.js:

import * as adders from './adders.js'
// Set up your object however you want. 
const MathStuff = {
  ...adders
}
export default MathStuff

Тогда в любом компоненте вы можете сделать это:

import MathStuff from './path/to/mathStuff'

MathStuff.addTwo(7) // 9

Для еще большей организации вы можете настроить свой индекс так, чтобы он имел такие функции:

index.js:

import * as adders from './adders.js'
import * as dividers from './dividers.js' // another math file with division functions or something

// Set up your object however you want. 
const MathStuff = {
  adders,
  dividers
}
export default MathStuff

И используйте это так:

import MathStuff from './path/to/mathStuff' // points to directory, NOT individual file

MathStuff.adders.addTwo(7) // 9

Я бы определенно предложил организовать такой код. Это улучшает тестируемость - очень просто тестировать чистые функции без побочных эффектов.

Мне нравится помещать код моей базы данных в один модуль и импортировать его куда угодно, чтобы получить доступ ко всем функциям моей базы данных.

Мне нравится размещать всю свою бизнес-логику в разных модулях по категориям - например, GameLogic или что-то типа того.

Это также поможет вам написать более функциональный код. В настоящее время у вас есть много изменений состояния в отдельных функциях - вы не сможете сделать это в модулях без привязки отдельных функций к контексту вашего компонента реакции. Вместо этого я бы предложил передать все необходимые параметры в функцию и вернуть ей значение. Это отодвигает бизнес-логику, облегчая управление состоянием.

Например, ваш progressMission доступ к функциям this.state.interval, Вы можете пройти interval к самой функции.

Одна вещь, на которую я обращаю внимание, это то, что ваш код сильно зависит друг от друга - функциям часто приходится получать доступ ко многим вещам вне себя, а не к самодостаточности. Возможно, вам очень поможет попытаться преобразовать в модульную систему, где функции намного более чисты - только доступ к тому, что им передано, и возврат значений, которые используются. Использование реальных модулей, как указано выше, определенно помогает в этом - мой код становится лучше, чем больше я это делал. Это поможет вам лучше понять ваш код. Кроме того, однажды / если вы начнете реализовывать тесты, вы обнаружите, что весь запутанный код затрудняет тестирование - есть много побочных эффектов.

Наконец, управление редукциями и внешним состоянием, вероятно, не поможет в вашем случае, но они могут. Redux может помочь вам достичь состояния, о котором легче рассуждать, но это не поможет вам лучше организовать код как таковой. Надеюсь, это поможет!

Другие вопросы по тегам