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! Используйте модули. Вот как это сделать.
- Экспорт функций из файла
adders.js
:
export function addTwo (number) {
return number + 2
}
- Тогда используйте это:
Это может быть в файле компонента:
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 может помочь вам достичь состояния, о котором легче рассуждать, но это не поможет вам лучше организовать код как таковой. Надеюсь, это поможет!