Рефакторинг компонента ReactJS - PullRequest
1 голос
/ 15 марта 2020

Я новичок в React, и я пытаюсь сделать простую кликерную игру. На данный момент все работает хорошо (на мой взгляд), но я думаю, что код очень повторяющийся (я использую много setState). Может кто-нибудь сказать мне, как сделать его более чистым и иметь дело с государством? Я не прошу вас реорганизовать этот код для меня просто для совета, как это сделать. Спасибо за любые советы.

Вот мой код:

import React, { Component } from 'react';

class Enemy extends Component {
  constructor(props) {
    super(props);

    this.state = {
      hp: 10,
      nextHp: 12,
      clickDamage: 1,
      gold: 0,
      dps: 0.1,
      num: 1,
      tapCount: 0,
      itemCount: 0,
      tapCost: 10,
      itemCost: 50
    };

    this.handleClick = this.handleClick.bind(this);
    this.buyTap = this.buyTap.bind(this);
    this.buyItem = this.buyItem.bind(this);
  }

  componentDidMount() {
    const interval = setInterval(() => {
      this.setState({ hp: this.state.hp -= this.state.dps });
      if(this.state.hp <= 0) {
        this.setState({ hp: this.state.hp = this.state.nextHp });
        this.setState({ nextHp: Math.floor(this.state.nextHp * 1.2) })
        this.setState({ gold: this.state.gold + 10});
        this.setState({ num: Math.floor(Math.random() * 4 + 1)});
      }
    }, 100);
  }

  handleClick() {
    this.setState({ hp: this.state.hp - this.state.clickDamage });

    if(this.state.hp <= 0) {
      this.setState({ hp: this.state.hp = this.state.nextHp });
      this.setState({ nextHp: Math.floor(this.state.nextHp * 1.2) })
      this.setState({ gold: this.state.gold + 10});
      this.setState({ num: Math.floor(Math.random() * 4 + 1)});
    }
  }

  buyTap() {
    if (this.state.gold >= this.state.tapCost) {
      this.setState({ gold: this.state.gold -= this.state.tapCost });
      this.setState({ tapCount: this.state.tapCount += 1 });
      this.setState({ clickDamage: this.state.clickDamage += 1 })
    }
  }

  buyItem() {
    if (this.state.gold >= this.state.itemCost) {
      this.setState({ gold: this.state.gold -= this.state.itemCost });
      this.setState({ itemCount: this.state.itemCount += 1 });
      this.setState({ dps: this.state.dps += 0.1 });
    }
  }

  render() {
    return (
      <div>
        <div className="container ui center aligned">
          <h1>Enemy hp: {Math.round(this.state.hp)}</h1>
          <h3>Gold: {this.state.gold}</h3>
          <h3>Click damage: {this.state.clickDamage}</h3>
          <h3>Damage per second: {Math.round(this.state.dps * 10)}</h3>
          <img
            alt=""
            className="dragon"
            src={require("../img/dragon" + this.state.num + ".jpg")}
            onClick={this.handleClick}
            draggable={false}
          />
        </div>
        <div className="ui container">
          <img
            onClick={this.buyTap}
            alt=""
            style={{ width: '50px' }}
            src={require("../img/tap.png")}
          />
          <p>Count: {this.state.tapCount}</p>
          <p>Cost: {this.state.tapCost}</p>
          <img
            onClick={this.buyItem}
            alt=""
            style={{ width: '50px' }}
            src={require("../img/rapier.png")}
          />
          <p>Count: {this.state.itemCount}</p>
          <p>Cost: {this.state.itemCost}</p>
        </div>
      </div>
    );
  }
}

export default Enemy;

Ответы [ 2 ]

3 голосов
/ 15 марта 2020

Не много, чтобы фактор. Но я могу сказать, что все ваши обновления состояния могут быть объединены. Вы можете обновить несколько значений состояния одновременно.

this.setState({ hp: this.state.hp = this.state.nextHp });
this.setState({ nextHp: Math.floor(this.state.nextHp * 1.2) })
this.setState({ gold: this.state.gold + 10});
this.setState({ num: Math.floor(Math.random() * 4 + 1)});

до

this.setState({
  hp: this.state.nextHp,
  nextHp: Math.floor(this.state.nextHp * 1.2),
  gold: this.state.gold + 10,
  num: Math.floor(Math.random() * 4 + 1)
});

Примечание на стороне : Мутации состояний, такие как this.state.hp = this.state.nextHp, являются анти-паттерном реакции и может привести к ошибкам.

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

this.setState(prevState => ({
  hp: prevState.nextHp,
  nextHp: Math.floor(prevState.nextHp * 1.2),
  gold: prevState.gold + 10,
  num: Math.floor(Math.random() * 4 + 1)
}));

Вот моя демонстрация codesandbox , которая иллюстрирует, почему это функциональное обновление может быть важным.

0 голосов
/ 15 марта 2020

В дополнение к объединению setState s в одно, как в ответе @Drew Reese, вы также можете избежать необходимости связывать методы (например, this.handleClick = this.handleClick.bind(this);), определяя их как функции стрелок в классе:

  handleClick = () => {
    // ...
  }

  buyTap = () => {
    // ...
  }

  buyItem = () => {
    // ...
  }

При этом вы также можете удалить конструктор из

  constructor(props) {
    super(props);

    this.state = {
      hp: 10,
      nextHp: 12,
      clickDamage: 1,
      gold: 0,
      dps: 0.1,
      num: 1,
      tapCount: 0,
      itemCount: 0,
      tapCost: 10,
      itemCost: 50
    };

    this.handleClick = this.handleClick.bind(this);
    this.buyTap = this.buyTap.bind(this);
    this.buyItem = this.buyItem.bind(this);
  }

до

// (state is defined without a constructor as an instance variable)
state = {
    hp: 10,
    nextHp: 12,
    clickDamage: 1,
    gold: 0,
    dps: 0.1,
    num: 1,
    tapCount: 0,
    itemCount: 0,
    tapCost: 10,
    itemCost: 50
};
...