第十五步:Home組件

2021-10-23 14:32 更新

第十五步:Home組件

這是一個稍微簡單些的組件,它唯一的職責(zé)就是顯示兩張圖片并且處理點擊事件,用于告知哪個角色勝出。

組件

在components目錄下新建文件Home.js

import React from 'react';
import {Link} from 'react-router';
import HomeStore from '../stores/HomeStore'
import HomeActions from '../actions/HomeActions';
import {first, without, findWhere} from 'underscore';

class Home extends React.Component {

  constructor(props) {
    super(props);
    this.state = HomeStore.getState();
    this.onChange = this.onChange.bind(this);
  }

  componentDidMount() {
    HomeStore.listen(this.onChange);
    HomeActions.getTwoCharacters();
  }

  componentWillUnmount() {
    HomeStore.unlisten(this.onChange);
  }

  onChange(state) {
    this.setState(state);
  }

  handleClick(character) {
    var winner = character.characterId;
    var loser = first(without(this.state.characters, findWhere(this.state.characters, { characterId: winner }))).characterId;
    HomeActions.vote(winner, loser);
  }

  render() {
    var characterNodes = this.state.characters.map((character, index) => {
      return (
        <div key={character.characterId} className={index === 0 ? 'col-xs-6 col-sm-6 col-md-5 col-md-offset-1' : 'col-xs-6 col-sm-6 col-md-5'}>
          <div className='thumbnail fadeInUp animated'>
            <img onClick={this.handleClick.bind(this, character)} src={'http://image.eveonline.com/Character/' + character.characterId + '_512.jpg'}/>
            <div className='caption text-center'>
              <ul className='list-inline'>
                <li><strong>Race:</strong> {character.race}</li>
                <li><strong>Bloodline:</strong> {character.bloodline}</li>
              </ul>
              <h4>
                <Link to={'/characters/' + character.characterId}><strong>{character.name}</strong></Link>
              </h4>
            </div>
          </div>
        </div>
      );
    });

    return (
      <div className='container'>
        <h3 className='text-center'>Click on the portrait. Select your favorite.</h3>
        <div className='row'>
          {characterNodes}
        </div>
      </div>
    );
  }
}

export default Home;

2015年7月27日更新:修復(fù)“Cannot read property ‘characterId’ of undefined”錯誤,我更新了在handleClick()方法里獲取“失敗”的Character ID。它使用_.findWhere在數(shù)組里查找“獲勝”的角色對象,然后使用_.without獲取不包含“獲勝”角色的數(shù)組,因為數(shù)組只包含兩個角色,所以這就是我們需要的,然后使用_.first獲取數(shù)組第一個元素,也就是我們需要的對象。

鑒于角色數(shù)組只有兩個元素,其實沒有必要非要使用map方法不可,雖然這也能達(dá)到我們的目的。另一種做法是為characters[0]characters[1]各自創(chuàng)建標(biāo)記。

render() {
    return (
      <div className='container'>
        <h3 className='text-center'>Click on the portrait. Select your favorite.</h3>
        <div className='row'>
          <div className='col-xs-6 col-sm-6 col-md-5 col-md-offset-1'>
            <div className='thumbnail fadeInUp animated'>
              <img onClick={this.handleClick.bind(this, characters[0])} src={'http://image.eveonline.com/Character/' + characters[0].characterId + '_512.jpg'}/>
              <div className='caption text-center'>
                <ul className='list-inline'>
                  <li><strong>Race:</strong> {characters[0].race}</li>
                  <li><strong>Bloodline:</strong> {characters[0].bloodline}</li>
                </ul>
                <h4>
                  <Link to={'/characters/' + characters[0].characterId}><strong>{characters[0].name}</strong></Link>
                </h4>
              </div>
            </div>
          </div>
          <div className='col-xs-6 col-sm-6 col-md-5'>
            <div className='thumbnail fadeInUp animated'>
              <img onClick={this.handleClick.bind(this, characters[1])} src={'http://image.eveonline.com/Character/' + characters[1].characterId + '_512.jpg'}/>
              <div className='caption text-center'>
                <ul className='list-inline'>
                  <li><strong>Race:</strong> {characters[1].race}</li>
                  <li><strong>Bloodline:</strong> {characters[1].bloodline}</li>
                </ul>
                <h4>
                  <Link to={'/characters/' + characters[1].characterId}><strong>{characters[1].name}</strong></Link>
                </h4>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

第一張圖片使用Bootstrap中的col-md-offset-1位移,所以兩張圖片是完美居中的。

注意我們在點擊事件上綁定的不是this.handleClick,而是this.handleClick.bind(this, character)。簡單的傳遞一個事件對象是不夠的,它不會給我們?nèi)魏斡杏玫男畔?,不像文本字段、單選、復(fù)選框元素等。

MSDN文檔中的解釋:

function.bind(thisArg[, arg1[, arg2[, ...]]])
  • thisARG(必須) – 使用this的一個對象,能在新函數(shù)內(nèi)部指向當(dāng)前對象的上下文
  • arg1, arg2, … (可選) – 傳遞給新函數(shù)的一系列參數(shù)

簡單的來說,因為我們需要在handleClick方法里引用this.state,所以需要將this上下文傳遞進去。另外我們還傳遞了被點擊的角色對象,而不是當(dāng)前的event對象。

handleClick方法里的character參數(shù)代表的是獲勝的角色,因為它是被點擊的那一個。因為我們僅有兩個角色需要判斷,所以不難分辨誰是輸?shù)哪莻€。接下來將獲勝和失敗的角色Character ID傳遞給Character ID action。

Actions

在actions目錄下新建HomeActions.js

import alt from '../alt';

class HomeActions {
  constructor() {
    this.generateActions(
      'getTwoCharactersSuccess',
      'getTwoCharactersFail',
      'voteFail'
    );
  }

  getTwoCharacters() {
    $.ajax({ url: '/api/characters' })
      .done(data => {
        this.actions.getTwoCharactersSuccess(data);
      })
      .fail(jqXhr => {
        this.actions.getTwoCharactersFail(jqXhr.responseJSON.message);
      });
  }

  vote(winner, loser) {
    $.ajax({
      type: 'PUT',
      url: '/api/characters' ,
      data: { winner: winner, loser: loser }
    })
      .done(() => {
        this.actions.getTwoCharacters();
      })
      .fail((jqXhr) => {
        this.actions.voteFail(jqXhr.responseJSON.message);
      });
  }
}

export default alt.createActions(HomeActions);

這里我們不需要voteSuccess action,因為getTwoCharacters已經(jīng)滿足了我們的需求。換句話說,在一次成功的投票之后,我們需要從數(shù)據(jù)庫獲取兩個新的隨機角色顯示出來。

Store

在stores目錄下新建文件HomeStore.js

import alt from '../alt';
import HomeActions from '../actions/HomeActions';

class HomeStore {
  constructor() {
    this.bindActions(HomeActions);
    this.characters = [];
  }

  onGetTwoCharactersSuccess(data) {
    this.characters = data;
  }

  onGetTwoCharactersFail(errorMessage) {
    toastr.error(errorMessage);
  }

  onVoteFail(errorMessage) {
    toastr.error(errorMessage);
  }
}

export default alt.createStore(HomeStore);

下一步,讓我們實現(xiàn)剩下的Express路由,來獲取并更新Home組件中的兩個角色、獲得總角色數(shù)量等等。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號