Апрацоўка падзеяў (Handling Events)

Апрацоўка падзеяў з React-элемэнтамі вельмі падобная да апрацоўкі падзеяў з элемэнтамі DOM. Але ёсьць некаторыя сынтаксычныя адрозьненьні:

  • Падзеям React даюцца імёны з выкарыстаньнем вярблюджага рэгістру (camelCase), а ня ніжняга рэгістру.
  • У выпадку JSX вы перадаяце функцыю як апрацоўшчык падзеяў, а не радок.

Так, напрыклад, HTML:

<button onclick="activateLasers()">
  Activate Lasers
</button>

крыху адрозьніваецца ў React:

<button onClick={activateLasers}>
  Activate Lasers
</button>

Іншае адрозьненьне ў тым, што вы ня можаце вярнуць false, каб прадухіліць паводзіны па змаўчаньні ў React. Вы павінны выклікаць preventDefault відавочна. Напрыклад, у выпадку простага HTML, каб не дапусьціць паводзінаў спасылкі па змаўчаньні — адкрыцьця новай старонкі, вы можаце напісаць:

<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>

У React замест гэнага можа быць так:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

Тут e зьяўляецца сынтэтычнай падзеяй. React азначае гэтыя сынтэтычныя падзеі згодна са спэцыфікацыяй W3C spec, так што вам ня трэба турбавацца аб сумяшчальнасьці з рознымі браўзэрамі. Глядзіце даведачны дапаможнік SyntheticEvent, каб даведацца больш.

Пры выкарыстаньні React у бальшыні выпадкаў вам ня трэба выклікаць addEventListener, каб дадаць слухачоў да элемэнта DOM пасьля яго стварэньня. Замест гэтага, проста прадстаўце слухача, калі гэты элемэнт першапачаткова візуалізаваны.

Калі вы азначаеце кампанэнт, выкарыстоўваючы кляс ES6, мэтадам клясу зьяўляецца агульны шаблён для апрацоўшчыка падзеяў. Напрыклад, гэты кампанэнт Toggle візуалізуе кнопку, якая дазваляе карыстальніку пераключацца паміж станамі “ON” ды “OFF”:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // Гэтае зьвязваньне неабходна, каб `this` працавала ў зваротным выкліку
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

Паспрабуйце на CodePen.

Будзьце асьцярожныя са значэньнем this у зваротных выкліках JSX. У JavaScript мэтады кляса ня зьвязаныя па змаўчаньні. Калі вы забудзеце зьвязаць this.handleClick і перадаць яго onClick, то this будзе undefined, калі функцыя будзе фактычна выклікана.

Гэтыя паводзіны не зьяўляюцца React-спэцыфічнымі; гэта частка таго, як функцыі працуюць у JavaScript. У агульным выпадку, калі вы зьвяртаецеся да мэтаду бяз () пасьля яго, напрыклад onClick={this.handleClick}, то вы павінны зьвязаць гэны мэтад.

Калі выкліканьне bind раздражняе вас, то ёсьць два спосабы, як гэта абысьці. Калі вы выкарыстоўваеце экспэрымэнтальны сынтаксіс палёў адкрытага кляса, то можаце выкарыстоўваць палі клясаў для правільнай прывязкі зваротных выклікаў:

class LoggingButton extends React.Component {
  // Гэты сынтаксіс забясьпечвае, каб `this` быў зьвязаны ў handleClick.
  // Увага: гэта *экспэрымэнтальны* сынтаксіс.
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

У Create React App гэты сынтаксіс уключаны па змаўчаньні.

Калі вы не выкарыстоўваеце сынтаксіс палёў кляса, то можаце скарыстаць стрэлкавую функцыю ў зваротным выкліку:

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // Гэты сынтаксіс забясьпечвае, каб `this` зьвязваўся ў handleClick
    return (
      <button onClick={(e) => this.handleClick(e)}>
        Click me
      </button>
    );
  }
}

Праблема з гэтым сынтаксісам у тым, што кожны раз, калі рэндэрыць LoggingButton, ствараецца іншы зваротны выклік. У большасьці выпадкаў гэта файна. Аднак, калі гэты зваротны выклік перадаецца ў якасьці prop да ніжэйшых кампанэнтаў, то тыя кампанэнты могуць зрабіць дадатковы паўторны рэндэрынг. Звычайна мы рэкамэндуем зьвязваньне ў канструктары або выкарыстаньне сынтаксісу палёў кляса, каб пазьбегнуць такога роду праблемаў прадукцыйнасьці.

Перадача аргумэнтаў да апрацоўшчыкаў падзеяў

Унутры цыкла звычайна патрабуецца перадаць дадатковы парамэтар да апрацоўшчыка падзеяў. Напрыклад, калі id зьяўляецца ідэнтыфікатарам радка (row ID), то будзе працаваць адно з наступных:

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

Два вышэйпрыведзеныя радкі зьяўляюцца эквівалентнымі й выкарыстоўваюць, адпаведна, стрэлкавыя функцыі ды Function.prototype.bind.

У абодвух выпадках аргумэнт e, які ўяўляе сабой падзею React, будзе перадавацца ў якасьці другога аргумэнта пасьля ID. З дапамогай стрэлкавай функцыі мы павінны перадаваць яго ў відавочным выглядзе, а з bind любыя дадатковыя аргумэнты перасылаюцца аўтаматычна.