Π­Ρ‚ΠΎΡ‚ сайт большС Π½Π΅ обновляСтся.ΠŸΠ΅Ρ€Π΅ΠΉΠ΄ΠΈΡ‚Π΅ Π½Π° react.dev

ΠŸΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ Π² ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹

Как ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ события (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, onClick) ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρƒ?

ΠŸΠ΅Ρ€Π΅Π΄Π°Π²Π°ΠΉΡ‚Π΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ событий ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Ρ‡Π΅Ρ€Π΅Π· пропсы Π΄ΠΎΡ‡Π΅Ρ€Π½ΠΈΠΌ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°ΠΌ:

<button onClick={this.handleClick}>

Если Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΠΈΠΌΠ΅Ρ‚ΡŒ доступ ΠΊ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρƒ-Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŽ Ρ‡Π΅Ρ€Π΅Π· ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ, Π²Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€ΠΈΠ²ΡΠ·Π°Ρ‚ΡŒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΊ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ€Ρƒ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° (см. Π½ΠΈΠΆΠ΅).

Как ΠΏΡ€ΠΈΠ²ΡΠ·Π°Ρ‚ΡŒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΊ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ€Ρƒ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°?

Π’ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΠΈ ΠΎΡ‚ Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊΠΎΠΉ синтаксис ΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ ΠΊ ΡΠΎΠ·Π΄Π°Π½ΠΈΡŽ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² Π²Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚Π΅, сущСствуСт нСсколько способов ΡƒΠ΄ΠΎΡΡ‚ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΈΠΌΠ΅ΡŽΡ‚ доступ ΠΊ Ρ‚Π°ΠΊΠΈΠΌ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Π°ΠΌ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°, ΠΊΠ°ΠΊ this.props ΠΈ this.state.

ΠŸΡ€ΠΈΠ²ΡΠ·ΠΊΠ° Π² ΠΊΠΎΠ½ΡΡ‚Ρ€ΡƒΠΊΡ‚ΠΎΡ€Π΅ (ES2015)

class Foo extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log('По ΠΊΠ½ΠΎΠΏΠΊΠ΅ ΠΊΠ»ΠΈΠΊΠ½ΡƒΠ»ΠΈ');
  }
  render() {
    return <button onClick={this.handleClick}>НаТми на мСня</button>;
  }
}

Бвойства класса (ES2022)

class Foo extends Component {
  handleClick = () => {
    console.log('По ΠΊΠ½ΠΎΠΏΠΊΠ΅ ΠΊΠ»ΠΈΠΊΠ½ΡƒΠ»ΠΈ');
  };
  render() {
    return <button onClick={this.handleClick}>НаТми на мСня</button>;
  }
}

ΠŸΡ€ΠΈΠ²ΡΠ·ΠΊΠ° Π² ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ render()

class Foo extends Component {
  handleClick() {
    console.log('По ΠΊΠ½ΠΎΠΏΠΊΠ΅ ΠΊΠ»ΠΈΠΊΠ½ΡƒΠ»ΠΈ');
  }
  render() {
    return <button onClick={this.handleClick.bind(this)}>НаТми на мСня</button>;
  }
}

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅:

ИспользованиС Function.prototype.bind Π² render() создаёт Π½ΠΎΠ²ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΏΡ€ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΌ Ρ€Π΅Π½Π΄Π΅Ρ€Π΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°, Ρ‡Ρ‚ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠ²Π»ΠΈΡΡ‚ΡŒ Π½Π° ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ (см. Π½ΠΈΠΆΠ΅).

БтрСлочная функция Π² render()

class Foo extends Component {
  handleClick() {
    console.log('По ΠΊΠ½ΠΎΠΏΠΊΠ΅ ΠΊΠ»ΠΈΠΊΠ½ΡƒΠ»ΠΈ');
  }
  render() {
    return <button onClick={() => this.handleClick()}>НаТми на мСня</button>;
  }
}

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅:

ИспользованиС стрСлочной Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π² render() создаёт Π½ΠΎΠ²ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΏΡ€ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ отрисовкС ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°, Ρ‡Ρ‚ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°Ρ€ΡƒΡˆΠ°Ρ‚ΡŒ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‰ΠΈΠ΅ строгоС сравнСниС для опрСдСлСния идСнтичности.

МоТно Π»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ стрСлочныС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π² ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ render()?

Π’ Ρ†Π΅Π»ΠΎΠΌ, Π΄Π°. Π—Π°Ρ‡Π°ΡΡ‚ΡƒΡŽ это самый простой способ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Ρ‡Π΅Ρ€Π΅Π· колбэки.

Если ΠΆΠ΅ Ρƒ Π²Π°Ρ Π²ΠΎΠ·Π½ΠΈΠΊΠ»ΠΈ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ с ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ β€” ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΡƒΠΉΡ‚Π΅!

Π—Π°Ρ‡Π΅ΠΌ Π²ΠΎΠΎΠ±Ρ‰Π΅ Π½ΡƒΠΆΠ½Π° привязка?

Π’ JavaScript эти Π΄Π²Π° Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚Π° ΠΊΠΎΠ΄Π° Π½Π΅ Ρ€Π°Π²Π½ΠΎΠ·Π½Π°Ρ‡Π½Ρ‹:

obj.method();
var method = obj.method;
method();

ΠŸΡ€ΠΈΠ²ΡΠ·ΠΊΠ° Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ Π²Ρ‚ΠΎΡ€ΠΎΠΉ Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ.

Π’ React, ΠΊΠ°ΠΊ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ, ΠΏΡ€ΠΈΠ²ΡΠ·Ρ‹Π²Π°Ρ‚ΡŒ Π½ΡƒΠΆΠ½ΠΎ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‚Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π²Ρ‹ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ Π΄Ρ€ΡƒΠ³ΠΈΠΌ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°ΠΌ. НапримСр, <button onClick={this.handleClick}> ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘Ρ‚ this.handleClick, поэтому Π΅Π³ΠΎ Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€ΠΈΠ²ΡΠ·Π°Ρ‚ΡŒ. Π’ΠΏΡ€ΠΎΡ‡Π΅ΠΌ, ΠΌΠ΅Ρ‚ΠΎΠ΄ render ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΆΠΈΠ·Π½Π΅Π½Π½ΠΎΠ³ΠΎ Ρ†ΠΈΠΊΠ»Π° ΠΏΡ€ΠΈΠ²ΡΠ·Ρ‹Π²Π°Ρ‚ΡŒ Π½Π΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ ΠΌΡ‹ Π½Π΅ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘ΠΌ ΠΈΡ… Π² Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹.

ΠžΠ·Π½Π°ΠΊΠΎΠΌΡŒΡ‚Π΅ΡΡŒ со ΡΡ‚Π°Ρ‚ΡŒΡ‘ΠΉ Π™Π΅Ρ…ΡƒΠ΄Ρ‹ ΠšΠ°Ρ‚Ρ†, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Π±ΠΎΠ»Π΅Π΅ ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ объяснСно, Ρ‡Ρ‚ΠΎ Ρ‚Π°ΠΊΠΎΠ΅ привязка, ΠΈ ΠΊΠ°ΠΊ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π² JavaScript.

ΠŸΠΎΡ‡Π΅ΠΌΡƒ моя функция вызываСтся ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π· ΠΏΡ€ΠΈ отрисовкС ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°?

Π£Π±Π΅Π΄ΠΈΡ‚Π΅ΡΡŒ, Ρ‡Ρ‚ΠΎ Π²Ρ‹ Π½Π΅ Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ, ΠΊΠΎΠ³Π΄Π° ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‘Ρ‚Π΅ Π΅Ρ‘ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρƒ:

render() {
  // ΠΠ΅ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ: вмСсто ссылки Π±Ρ‹Π»Π° Π²Ρ‹Π·Π²Π°Π½Π° функция handleClick!
  return <button onClick={this.handleClick()}>НаТми на мСня</button>
}

ВмСсто этого ΠΏΠ΅Ρ€Π΅Π΄Π°ΠΉΡ‚Π΅ саму Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ (Π±Π΅Π· скобок):

render() {
  // ΠŸΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ: handleClick пСрСдаётся ΠΊΠ°ΠΊ ссылка!
  return <button onClick={this.handleClick}>НаТми на мСня</button>
}

Как ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΡƒ событий ΠΈΠ»ΠΈ колбэку?

Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΡƒ событий, ΠΎΠ±Π΅Ρ€Π½ΠΈΡ‚Π΅ Π΅Π³ΠΎ Π² ΡΡ‚Ρ€Π΅Π»ΠΎΡ‡Π½ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ:

<button onClick={() => this.handleClick(id)} />

Π­Ρ‚ΠΎ дСйствиС Ρ€Π°Π²Π½ΠΎΡΠΈΠ»ΡŒΠ½ΠΎ использованию .bind:

<button onClick={this.handleClick.bind(this, id)} />

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: ΠŸΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² с ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ стрСлочных Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ

const A = 65 // ASCII-код символа

class Alphabet extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      justClicked: null,
      letters: Array.from({length: 26}, (_, i) => String.fromCharCode(A + i))
    };
  }
  handleClick(letter) {
    this.setState({ justClicked: letter });
  }
  render() {
    return (
      <div>
        Just clicked: {this.state.justClicked}
        <ul>
          {this.state.letters.map(letter =>
            <li key={letter} onClick={() => this.handleClick(letter)}>
              {letter}
            </li>
          )}
        </ul>
      </div>
    )
  }
}

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: ΠŸΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² с ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ΠΎΠ² Π΄Π°Π½Π½Ρ‹Ρ…

Π’ ΠΊΠ°Ρ‡Π΅ΡΡ‚Π²Π΅ Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½ΠΎΠ³ΠΎ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π° Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ DOM API, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² событий Π΄Π°Π½Π½Ρ‹Π΅. РассмотритС этот ΠΏΠΎΠ΄Ρ…ΠΎΠ΄, Ссли Π²Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ большоС количСство элСмСнтов ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΄Π΅Ρ€Π΅Π²ΠΎ Π²ΠΈΠ·ΡƒΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ, ΠΏΠΎΠ»Π°Π³Π°ΡŽΡ‰Π΅Π΅ΡΡ Π½Π° ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ React.PureComponent для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Π½Π° Ρ€Π°Π²Π΅Π½ΡΡ‚Π²ΠΎ.

const A = 65 // ASCII-код символа

class Alphabet extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.state = {
      justClicked: null,
      letters: Array.from({length: 26}, (_, i) => String.fromCharCode(A + i))
    };
  }

  handleClick(e) {
    this.setState({
      justClicked: e.target.dataset.letter
    });
  }

  render() {
    return (
      <div>
        Just clicked: {this.state.justClicked}
        <ul>
          {this.state.letters.map(letter =>
            <li key={letter} data-letter={letter} onClick={this.handleClick}>
              {letter}
            </li>
          )}
        </ul>
      </div>
    )
  }
}

Как ΠΏΡ€Π΅Π΄ΠΎΡ‚Π²Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ слишком быстрый ΠΈΠ»ΠΈ слишком частый Π²Ρ‹Π·ΠΎΠ² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ?

Если Π²Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚Π΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ событий, Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ onClick ΠΈΠ»ΠΈ onScroll, ΠΈ Ρ…ΠΎΡ‚ΠΈΡ‚Π΅ ΠΏΡ€Π΅Π΄ΠΎΡ‚Π²Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ быстроС срабатываниС колбэков, Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚ΡŒ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ выполнСния колбэка. Для этого Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ:

  • Ρ‚Ρ€ΠΎΡ‚Ρ‚Π»ΠΈΠ½Π³: Π²Ρ‹Π±ΠΎΡ€ΠΎΡ‡Π½Ρ‹Π΅ измСнСния, зависимыС ΠΎΡ‚ Ρ‡Π°ΡΡ‚ΠΎΡ‚Ρ‹, основанной Π½Π° Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ (Π½Π°ΠΏΡ€. _.throttle)
  • дСбаунсинг: измСнСния, задСйствованныС послС Π½Π΅ΠΊΠΎΠ³ΠΎ ΠΏΠ΅Ρ€ΠΈΠΎΠ΄Π° бСздСйствия (Π½Π°ΠΏΡ€. _.debounce)
  • Ρ‚Ρ€ΠΎΡ‚Ρ‚Π»ΠΈΠ½Π³ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ requestAnimationFrame: Π²Ρ‹Π±ΠΎΡ€ΠΎΡ‡Π½Ρ‹Π΅ измСнСния, основанныС Π½Π° requestAnimationFrame (Π½Π°ΠΏΡ€. raf-schd)

ВзглянитС Π½Π° Π²ΠΈΠ·ΡƒΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ, которая сравниваСт Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ throttle ΠΈ debounce.

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅:

_.debounce, _.throttle ΠΈ raf-schd ΠΏΡ€Π΅Π΄ΡƒΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°ΡŽΡ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄ cancel для ΠΎΡ‚ΠΌΠ΅Π½Ρ‹ ΠΎΡ‚Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… колбэков. Π’Ρ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π»ΠΈΠ±ΠΎ Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ этот ΠΌΠ΅Ρ‚ΠΎΠ΄ ΠΈΠ· componentWillUnmount, Π»ΠΈΠ±ΠΎ ΡƒΠ΄ΠΎΡΡ‚ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ всё Π΅Ρ‰Ρ‘ встроСн Π² ΠΏΡ€Π΅Π΄Π΅Π»Π°Ρ… ΠΎΡ‚Π»ΠΎΠΆΠ΅Π½Π½ΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ.

Throttle

Π’Ρ€ΠΎΡ‚Ρ‚Π»ΠΈΠ½Π³ ΠΏΡ€Π΅Π΄ΠΎΡ‚Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½Ρ‹ΠΉ Π²Ρ‹Π·ΠΎΠ² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π² Π·Π°Π΄Π°Π½Π½Ρ‹ΠΉ ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ. Π­Ρ‚ΠΎΡ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄ Π±Ρ‹Π» задСйствован Π² ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π½ΠΈΠΆΠ΅, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ Π΄ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ Π²Ρ‹Π·ΠΎΠ² ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ° Β«clickΒ» Ρ‡Π°Ρ‰Π΅ Ρ‡Π΅ΠΌ Ρ€Π°Π· Π² ΡΠ΅ΠΊΡƒΠ½Π΄Ρƒ.

import throttle from 'lodash.throttle';

class LoadMoreButton extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.handleClickThrottled = throttle(this.handleClick, 1000);
  }

  componentWillUnmount() {
    this.handleClickThrottled.cancel();
  }

  render() {
    return <button onClick={this.handleClickThrottled}>Π—Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ Π΅Ρ‰Ρ‘</button>;
  }

  handleClick() {
    this.props.loadMore();
  }
}

Debounce

ДСбаунсинг Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ функция Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒΡΡ Π΄ΠΎ Ρ‚Π΅Ρ… ΠΏΠΎΡ€, ΠΏΠΎΠΊΠ° Π½Π΅ ΠΏΡ€ΠΎΠΉΠ΄Ρ‘Ρ‚ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Ρ‘Π½Π½ΠΎΠ΅ количСство Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ с ΠΌΠΎΠΌΠ΅Π½Ρ‚Π° Π΅Ρ‘ ΠΏΠΎΡΠ»Π΅Π΄Π½Π΅Π³ΠΎ Π²Ρ‹Π·ΠΎΠ²Π°. Π­Ρ‚ΠΎΡ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄ пригодится, Ссли Π²Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ провСсти рСсурсоёмкий расчёт Π² ΠΎΡ‚Π²Π΅Ρ‚ Π½Π° ΡΠΎΠ±Ρ‹Ρ‚ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ быстро ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΠΈΡ‚ΡŒΡΡ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΏΡ€ΠΎΠΊΡ€ΡƒΡ‚ΠΊΠ° страницы ΠΈΠ»ΠΈ Π½Π°ΠΆΠ°Ρ‚ΠΈΠ΅ клавиш). Π’ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π½ΠΈΠΆΠ΅ для Π²Π²ΠΎΠ΄Π° тСкста ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π·Π°Π΄Π΅Ρ€ΠΆΠΊΠ° Π² 250 ΠΌΡ.

import debounce from 'lodash.debounce';

class Searchbox extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.emitChangeDebounced = debounce(this.emitChange, 250);
  }

  componentWillUnmount() {
    this.emitChangeDebounced.cancel();
  }

  render() {
    return (
      <input
        type="text"
        onChange={this.handleChange}
        placeholder="Поиск..."
        defaultValue={this.props.value}
      />
    );
  }

  handleChange(e) {
    this.emitChangeDebounced(e.target.value);
  }

  emitChange(value) {
    this.props.onChange(value);
  }
}

requestAnimationFrame throttling

requestAnimationFrame β€” это способ ΠΎΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, которая Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½Π° Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅ Π·Π° ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»ΡŒΠ½ΠΎΠ΅ врСмя для ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ отрисовки. Ѐункция, поставлСнная Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ requestAnimationFrame, запустится Π² ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ ΠΊΠ°Π΄Ρ€Π΅. Π‘Ρ€Π°ΡƒΠ·Π΅Ρ€ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠΈΡ‚ всС усилия, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΠ±Π΅ΡΠΏΠ΅Ρ‡ΠΈΡ‚ΡŒ 60 ΠΊΠ°Π΄Ρ€ΠΎΠ² Π² ΡΠ΅ΠΊΡƒΠ½Π΄Ρƒ (60 fps β€” frames per second). Однако, Ссли Π±Ρ€Π°ΡƒΠ·Π΅Ρ€ Π½Π΅ Π² ΡΠΎΡΡ‚оянии ΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒΡΡ с ΡΡ‚ΠΎΠΉ Π·Π°Π΄Π°Ρ‡Π΅ΠΉ, ΠΎΠ½ Π΅ΡΡ‚СствСнным ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚ количСство ΠΊΠ°Π΄Ρ€ΠΎΠ² Π² ΡΠ΅ΠΊΡƒΠ½Π΄Ρƒ. НапримСр, Ссли вашС устройство ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ 30 fps, Ρ‚ΠΎ ΠΈ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚Π΅ Π²Ρ‹ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ 30 ΠΊΠ°Π΄Ρ€ΠΎΠ². ИспользованиС requestAnimationFrame для Ρ‚Ρ€ΠΎΡ‚Ρ‚Π»ΠΈΠ½Π³Π° являСтся ΠΎΡ‡Π΅Π½ΡŒ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠΌ, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ‚ ΠΏΡ€Π΅Π΄ΠΎΡ‚Π²Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Π±ΠΎΠ»Π΅Π΅ 60 ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ Π² ΡΠ΅ΠΊΡƒΠ½Π΄Ρƒ. Если Π²Ρ‹ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΠ΅Ρ‚Π΅ 100 ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ Π² ΡΠ΅ΠΊΡƒΠ½Π΄Ρƒ, это создаёт лишнюю Ρ€Π°Π±ΠΎΡ‚Ρƒ для Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π°, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ всё Ρ€Π°Π²Π½ΠΎ Π½Π΅ Π·Π°ΠΌΠ΅Ρ‚ΠΈΡ‚.

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅:

ИспользованиС этой Ρ‚Π΅Ρ…Π½ΠΈΠΊΠΈ Π·Π°Ρ…Π²Π°Ρ‚ΠΈΡ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ послСднСС ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½Π½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π² ΠΊΠ°Π΄Ρ€Π΅. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π΄Π°Π½Π½ΠΎΠΉ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ Π½Π° MDN

import rafSchedule from 'raf-schd';

class ScrollListener extends React.Component {
  constructor(props) {
    super(props);

    this.handleScroll = this.handleScroll.bind(this);

    // Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ Π½ΠΎΠ²ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ для планирования ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ.
    this.scheduleUpdate = rafSchedule(
      point => this.props.onScroll(point)
    );
  }

  handleScroll(e) {
    // ΠŸΠ»Π°Π½ΠΈΡ€ΡƒΠ΅ΠΌ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΈ Π°ΠΊΡ‚ΠΈΠ²ΠΈΠ·Π°Ρ†ΠΈΠΈ события ΠΏΡ€ΠΎΠΊΡ€ΡƒΡ‚ΠΊΠΈ.
    // Если Π² Ρ€Π°ΠΌΠΊΠ°Ρ… ΠΊΠ°Π΄Ρ€Π° ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΌΠ½ΠΎΠ³ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ, ΠΏΡƒΠ±Π»ΠΈΠΊΡƒΠ΅ΠΌ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ послСднСС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
    this.scheduleUpdate({ x: e.clientX, y: e.clientY });
  }

  componentWillUnmount() {
    // ΠžΡ‚ΠΌΠ΅Π½ΡΠ΅ΠΌ Π»ΡŽΠ±Ρ‹Π΅ ΠΎΠΆΠΈΠ΄Π°ΡŽΡ‰ΠΈΠ΅ обновлСния, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ Π±ΡƒΠ΄Π΅Ρ‚ Π΄Π΅ΠΌΠΎΠ½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½.
    this.scheduleUpdate.cancel();
  }

  render() {
    return (
      <div
        style={{ overflow: 'scroll' }}
        onScroll={this.handleScroll}
      >
        <img src="/my-huge-image.jpg" />
      </div>
    );
  }
}

ВСстированиС ограничСния скорости

Когда Π²Ρ‹ Ρ‚СстируСтС, Ρ‡Ρ‚ΠΎ ваш ΠΊΠΎΠ΄ ограничСния скорости Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ, ΠΏΠΎΠ»Π΅Π·Π½ΠΎ ΠΈΠΌΠ΅Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΎΠΊΡ€ΡƒΡ‚ΠΈΡ‚ΡŒ врСмя. Если Π²Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚Π΅ jest, Π²Π°ΠΌ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€ΠΈΠ³ΠΎΠ΄ΠΈΡ‚ΡŒΡΡ mock timers. Если Π²Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚Π΅ requestAnimationFrame, Ρ‚ΠΎ raf-stub ΠΌΠΎΠΆΠ΅Ρ‚ ΠΎΠΊΠ°Π·Π°Ρ‚ΡŒΡΡ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ΠΌ инструмСнтом для управлСния смСны ΠΊΠ°Π΄Ρ€ΠΎΠ² Π°Π½ΠΈΠΌΠ°Ρ†ΠΈΠΈ.