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əyicisini valideyn komponentdən istifadə etdikdə funksiyanı komponent instansiyasına bind etmək lazımdır (aşağı bölməyə baxın).

Funksiyanı komponent instansiyasına necə bind etmək olar?

Funksiyalardan this.propsthis.state kimi atributların istifadəsinin qurulma addımından və sintaksisdən asılı olaraq 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>;
  }
}

Klas 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ən 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ən çağrıldı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ərinin optimallaşdırılmasını 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 optimizasiya edin!

Binding Niyə Vacibdir?

JavaScript-də bu iki kod bərabər deyil:

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

İkinci kod ilə birinci kodun eyni işləməsi üçün binding 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ərdiyindən bu funksiyanı bind edin. 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ə binding-in nə olduğu və JavaScript-də funksiyaların necə işlədiyini haqqında izahatlar var.

Niyə funksiya, komponent render edildiyi zamanı çağrılır?

Funksiyanı komponentə göndərdikdə bu funksiyanı çağırmayın:

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>
}

Ə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ərin ox funksiyalarına göndərilməsi

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ərin data-atributlarına göndərilməsi

Alternativ olaraq, DOM API-ından 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şmadan istifadə edin.

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 tez-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ılmasının sürətini aşağıdakl yollar ilə məhdudlaşdıra bilərsiniz:

  • boğmaq (throttle): yeniliklərin vaxt tezliyinə görə seçin (məsələn, _.throttle)
  • debounce edin: hərəkətsizlik olduqdan sonra yenilikləri dərc edin (məsələn, _.debounce)
  • requestAnimationFrame ilə boğmaq: yenilikləri requestAnimationFrame əsasında seçin (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ən komponentin mount olunduğunu yoxlamalısınız.

Boğma

Boğma, 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 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ə boğmaq

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ı boğma üçü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 boğmasından istifadə etdikdə animasiya kadrlarını idarə etmək üçün raf-stub alətini faydalı tapa bilərsiniz.