Funksiyaların Komponentlərə Göndərilməsi

Komponentə onClick kimi hadisə işləyicisini necə göndərə bilərəm?

Hadisə işləyicilərini və digər funksiyaları uşaq komponentlərə proplar kimi göndərin:

<button onClick={this.handleClick}>

Hadisə işləyicisində valideyn komponentdən istifadə etmək istədikdə, funksiyanı komponent instansiyasına bind etmək lazımdır (aşağı bölməyə baxın).

Funksiyanı komponent instansiyasına necə bind edə bilərəm?

Qurulma addımından və sintaksisdən asılı olaraq funksiyaların this.propsthis.state kimi atributları istifadə etməsi üçün bir neçə yolu var.

Konstruktorda Bind etmək (ES2015)

class Foo extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log('Tıklama Hadisəsi Baş Verdi');
  }
  render() {
    return <button onClick={this.handleClick}>Tıkla</button>;
  }
}

Sinif Parametrləri (3-cü Mərhələ Təklifi)

class Foo extends Component {
  // Qeyd: bu sintaksis experimentaldır və hələ standartlaşmayıb.
  handleClick = () => {
    console.log('Tıklama Hadisəsi Baş Verdi');
  }
  render() {
    return <button onClick={this.handleClick}>Tıkla</button>;
  }
}

Render-də Bind etmək

class Foo extends Component {
  handleClick() {
    console.log('Tıklama hadisəsi baş verdi');
  }
  render() {
    return <button onClick={this.handleClick.bind(this)}>Tıkla</button>;
  }
}

Qeyd:

Function.prototype.bind funksiyanı render-də çağırdıqda komponentin hər render edilməsi zamanı yeni funksiya yaranacaq. Bunun performansa təsiri ola bilər (aşağıdakı bölmələrə baxın).

Render-də Ox Funksiyası

class Foo extends Component {
  handleClick() {
    console.log('Tıklama Hadisəsi Baş Verdi');
  }
  render() {
    return <button onClick={() => this.handleClick()}>Tıkla</button>;
  }
}

Qeyd:

Render-dən ox funksiyası işlədildikdə komponentin hər render edilməsi zamanı yeni funksiya yaranacaq. Bu, identiklik müqayisələrə dayalı optimallaşdırılmaları sındıra bilər.

Render funksiyalarında ox funksiyalarını işlətmək olar?

Normalda, olar. Bu, callback funksiyalarına arqumentlər göndərməyin ən asan yoludur.

Performans problemləri olduqda optimallaşdırma edin!

Bind etmə niyə vacibdir?

JavaScript-də aşağıdakı iki kod parçası bərabər deyil:

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

İkinci kod parçası ilə birinci kod parçasının eyni işləməsi üçün bind etmə lazımdır.

React-də, adətən digər komponentlərə göndərilən funksiyaları bind etmək lazımdır. Məsələn, <button onClick={this.handleClick}> kodu this.handleClick funksiyasını göndərdiyinə görə bu funksiyanı bind etmək lazımdır. Lakin, render və ya lifecycle funksiyalarını bind etmək lazım deyil. Biz bu funksiyaları digər komponentlərə göndərmirik.

Yehuda Katzın bu məqaləsində bind etmənin nə olduğu və JavaScript-də funksiyaların necə işlədiyi haqda izahatlar var.

Niyə funksiyam komponent hər render edildiyi zaman çağrılır?

Funksiyanı komponentə göndərdikdə bu funksiyanı çağırmadığınızdan əmin olun:

render() {
  // Yanlışdır: handleClick funksiyası referans kimi göndərilmək əvəzinə çağrılır!
  return <button onClick={this.handleClick()}>Tıkla</button>
}

Bunun əvəzinə, funksiyanın özünü göndərin (mötərizəsiz):

render() {
  // Düzdür: handleClick funksiyası referans kimi göndərilir!
  return <button onClick={this.handleClick}>Tıkla</button>
}

Callback və Hadisə işləyicilərinə arqumentləri necə göndərə bilərəm?

Ox funksiyası ilə hadisə işləyicisini əhatə edərək arqumentləri göndərmək mümkündür:

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

Bu, .bind funksiyasının çağrılmasına bərabərdir:

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

Nümunə: Arqumentləri ox funksiyaları ilə göndərmək

const A = 65 // ASCII hərf kodu

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(letter) {
    this.setState({ justClicked: letter });
  }
  render() {
    return (
      <div>
        Tıklandı: {this.state.justClicked}
        <ul>
          {this.state.letters.map(letter =>
            <li key={letter} onClick={() => this.handleClick(letter)}>
              {letter}
            </li>
          )}
        </ul>
      </div>
    )
  }
}

Nümunə: Arqumentləri data-atributları ilə göndərmək

Alternativ olaraq, DOM API-ları istifadə edərək hadisə işləyiciləri üçün lazım olan məlumatları saxlaya bilərsiniz. Böyük sayda elementləri optimallaşdırmaq və ya React.PureComponent yoxlamalarından asılı olan render ağacından istifadə etmək istəyirsinizsə, bu yanaşmanı nəzərə alın.

const A = 65 // ASCII hərf kodu

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>
        Tıklandı: {this.state.justClicked}
        <ul>
          {this.state.letters.map(letter =>
            <li key={letter} data-letter={letter} onClick={this.handleClick}>
              {letter}
            </li>
          )}
        </ul>
      </div>
    )
  }
}

Funksiyanın çox tez və ya eyni zamanda bir neçə dəfə çağrılmasının qarşısını necə ala bilərəm?

Əgər onClick və ya onScroll kimi hadisə işləyicilərindən istifadə edir və bu callback-lərin tez çağrılmasının qarşısını almaq istəyirsinizsə, callback-in çağrılma sıxlığını aşağıdakı yollar ilə məhdudlaşdıra bilərsiniz:

  • throttle etmə: yenilikləri vaxt tezliyinə görə seçmə (məsələn, _.throttle)
  • debounce etmə: bir müddət fəaliyyətsizlik sonra yenilikləri əməl etmə (məsələn, _.debounce)
  • requestAnimationFrame ilə throttle etmə: yenilikləri requestAnimationFrame əsasında seçmə (məsələn, raf-schd)

throttledebounce funksiyaslarının müqayisəsi üçün bu görüntüyə baxın.

Qeyd:

_.debounce, _.throttleraf-schd funksiyaları gecikən callback-ləri ləğv etmək üçün cancel funksiyası təmin edirlər. Siz bu funksiyanı componentWillUnmount-dan çağırmalı və ya gecikən funksiyanın daxilində komponentin mount olunduğunu yoxlamalısınız.

Throttle

Throttle etmə, verilən vaxt çərçivəsində funksiyanın birdən çox çağrılmasının qarşısını alır. Aşağıdakı nümunədə “click” işləyicisinin bir saniyə ərzində birdən çox çağrılmasının qarşısı alınır.

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}>Əlavə yüklə</button>;
  }

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

Debounce

Debounce ilə funksiyanın son çağırışından bir qədər vaxt keçmədən çağrılmasının qarşısı alınır. Bu metod, tez-tez göndərilən hadisənin (məsələn, skrol və ya klaviatur hadisələri) cavabı nəticəsində bahalı hesablama apardıqda faydalıdır. Aşağıdakı nümunədə anket sakəsi 250ms gecikmə ilə yazılır.

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="Axtar..."
        defaultValue={this.props.value}
      />
    );
  }

  handleChange(e) {
    // React hadisələri pool-a əlavə etdiyindən biz dəyəri debounce-dan əvvəl oxuyuruq.
    // Alternativ olaraq, `event.persist()` funksiyasını çağıraraq bütün hadisəni göndərmək mümkündür.
    // Əlavə məlumat üçün az.reactjs.org/docs/events.html#event-pooling səhifəsinə baxın.
    this.emitChangeDebounced(e.target.value);
  }

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

requestAnimationFrame ilə throttle etmə

requestAnimationFrame funksiyası, göndərilən funksiyanı brauzerdə növbəyə salaraq render performansını artırmaq üçün bu funksiyanı optimal zamanda çağırır. requestAnimationFrame ilə növbələnən funksiya sonrakı kadrda çağrılacaq. Brauzer saniyəyə 60 kadrın olmasını (60 fps) təmin etmək üçün çox çalışacaq. Lakin, 60 fps təmin edilə bilmədikdə natural olaraq bir saniyəyə düşən kadrların sayı məhdudlaşdırılacaq. Məsələn, aparat yalnız 30 fps qəbul edə bilirsə, brauzer saniyəyə 30 kadr göstərəcək. Saniyəyə 60-dan çox yenilik etmənin qabağını almaq üçün requestAnimationFrame funksiyasını throttle etmək üçün istifadə etmək faydalıdır. Onsuzda, 100-dən çox yenilik edildikdə brauzerin icra edəcəyi işi istifadəçi görməyəcək.

Qeyd:

Bu texnika ilə bir kadrda dərc olunan ən sonuncu dəyər işlədiləcək. Bu optimizasiyanın işləməsini görmək üçün MDN-də olan nümunəyə baxın.

import rafSchedule from 'raf-schd';

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

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

    // Yenilikləri planlaşdırmaq üçün yeni funksiya yaradın.
    this.scheduleUpdate = rafSchedule(
      point => this.props.onScroll(point)
    );
  }

  handleScroll(e) {
    // Skrol hadisəsi gəldikdə yeniliyi planlaşdırın.
    // Bir kadr zamanı çoxlu yenilik baş verdikdə yalnız sonuncu dəyər dərc olunacaq.
    this.scheduleUpdate({ x: e.clientX, y: e.clientY });
  }

  componentWillUnmount() {
    // Unmount edildiyi zaman bütün gələcək yenilikləri ləğv edin.
    this.scheduleUpdate.cancel();
  }

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

Sürətin məhdudlaşdırılmasını test edin

Sürəti məhdudlaşan kodu test etdikdə vaxtı qabağa çəkmək qabiliyyətinin olması faydalı ola bilər. Jest işlədirsinizsə, vaxtı qabağa çəkmək üçün taymer moklarından istifadə edə bilərsiniz. requestAnimationFrame throttle etməsindən istifadə etdikdə animasiya kadrlarını idarə etmək üçün raf-stub alətini faydalı ola bilər.