We want to hear from you!Take our 2021 Community Survey!
This site is no longer updated.Go to react.dev

Dərslik: React-ə Giriş

These docs are old and won’t be updated. Go to react.dev for the new React docs.

The updated Tutorial teaches modern React and includes live examples.

Bu dərsliyə başlamaq üçün React biliyinizin olması vacib deyil.

Dərsliyi Başlamazdan Əvvəl

Bu dərslikdə biz kiçik bir oyun yazacağıq. Oyun tərtibatçısı olmadığınız üçün bu dərsliyi ötürmək istəyə bilərsiniz, ancaq cəhd etməyə dəyər. Bu dərslikdə öyrənəcəyiniz metodlar React applikasiyası yazmaq üçün əsasdır və bu metodları mənimsəmək sizə React-i dərindən başa düşməyə kömək edəcək.

Məsləhət

Bu dərslik praktika ilə öyrənməyə üstünlük verənlər üçün nəzərdə tutulmuşdur. Əgər siz konsepsiyaları sıfırdan öyrənməyə üstünlük verirsinizsə, bizim pillə-pillə təlimatımızı nəzərdən keçirin. Bu dərslik və göstərilən təlimat bir-birini tamamlaya bilər.

Bu dərslik bir neçə bölməyə bölünmüşdür:

Bu dərslikdən dəyər almaq üçün bütün bölmələri bir dəfəyə tamamlamaq vacib deyil. Çalışın mümkün qədər çox bölmədən keçəsiniz — hətta bir və ya iki bölmə olsa belə.

Biz nə düzəldirik?

Bu dərslikdə biz React ilə interaktiv “X O oyununu” düzəltməyi göstərəcəyik.

Bizim düzəltdiyimizə buradan baxa bilərsiniz: Son Nəticə. Əgər kod sizə aydın deyilsə və ya sintaksis sizə tanış deyilsə, narahat olmayın! Bu dərsliyin məqsədi sizə React və onun sintaksisini anlamağa kömək etməkdir.

Dərsliyə başlamamışdan qabaq oyuna baxmağınızı tövsiyə edirik. Bu oyunun diqqət edəcəyiniz xüsusiyyətlərindən biri oyun taxtasının sağında çıxan nömrələnmiş siyahıdır. Bu siyahı oyunda baş vermiş bütün hərəkətlərin tarixçəsidir və bu siyahı oyun davam etdikcə yenilənir.

Oyun ilə tanış olduqdan sonra oyun səhifəsini bağlaya bilərsiniz. Biz bu dərslikdə sadə bir şablon ilə başlayacağıq. Sonraki addım sizi oyunu yazmağa hazır etməkdir.

Ön şərtlər

Biz güman edirik ki, siz HTML və JavaScript-lə bir az tanışsınız, lakin, tanışlığınız fərqli proqramlaşdırma dilləriylə olduğu təqdirdə də davam edə bilərsiniz. Biz həmçinin güman edirik ki, siz proqramlaşdırmanın funksiyalar, obyektlər, massivlər və ən azı ilkin səviyyədə siniflər anlayışlarıyla tanışsınız.

Əgər JavaScripti nəzərdən keçirmək istəyirsinizsə, biz bu təlimatı oxumağınızı tövsiyə edirik. Diqqət yetirin ki, biz həmçinin arrow funksiyaları, siniflər, letconst operatorları kimi bəzi ES6 (Javascriptin son versiyası) özəlliklərindən istifadə edəcəyik. Siz Babel REPL istifadə edərək ES6 kodunun nəyə kompilyasiya olunduğunu görə bilərsiniz.

Dərslik Mühitinin Qurulması

Bu dərsliyi iki yol ilə tamamlamaq olar: siz kodu brauzerdə və ya lokal mühit yaradaraq kompüterinizdə yaza bilərsiniz.

Seçim 1: Kodu Brauzerdə Yaz

Bu başlamaq üçün ən sürətli üsuldur!

İlk olaraq, bu hazır kodu yeni tabda açın. Yeni tabda siz, boş “X O oyun” lövhəsini və bu dərslikdə tədricən dəyişdirəcəyimiz React kodu görəcəksiniz.

Siz indi ikinci seçimi ötürə və React barədə ümumi məlumat əldə etmək üçün İcmal bölməsinə keçə bilərsiniz.

Seçim 2: Lokal Təkmilləşmə Mühiti

Bu tamamilə istəyə bağlıdır və bu dərs üçün tələb olunmur!


İstəyə bağlıdır: Layihəni üstünlük verdiyiniz redaktor ilə yazmağınız üçün təlimat

Lokal mühitin sazlanması daha çox vaxt tələb edir, amma sizə bu dərsliyi seçdiyiniz redaktor ilə tamamlamağa imkan verir. Bunun üçün aşağıdakı addımlara əməl edin:

  1. Əmin olun ki, Node.js son versiyası qurulub.
  2. Yeni layihə yaratmaq üçün Create React App qurulma təlimatlarına əməl edin.
npx create-react-app my-app
  1. Yeni layihənin src/ direktoriyasından bütün faylları silin.

Qeyd:

Bütün src direktoriyasını yox, yalnız daxilindəki faylları silin.. Biz standart faylları bu layihədəki nümunələr ilə əvəz edəcəyik.

cd my-app
cd src

# Əgər Mac və ya Linux işlədirsinizsə:
rm -f *

# Əgər Windows işlədirsinizsə:
del *

# Layihə direktoriyasına geri qayıdın:
cd ..
  1. index.css adlı faylı src/ direktoriyasına bu CSS kodu ilə əlavə edin.
  2. index.js adlı faylı src/ direktoriyasına bu JS kodu ilə əlavə edin.
  3. src/ direktoriyasında olan index.js faylının başlanğıcına aşağıdakı üç sətri əlavə edin:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

İndi, əgər siz layihə direktoriyasından npm start icra etsəniz və brauzerdə http://localhost:3000 səhifəsini açsanız, boş “X O oyunu” lövhəsini görməlisiniz.

Redaktorunuzda sintaktik işıqlandırılmanı sazlamaq üçün bu təlimatlara əməl etməyiniz tövsiyə olunur.

Kömək Edin, Mən İlişmişəm!

Əgər ilişmisinizsə, cəmiyyətimizin dəstək resurslarını nəzərdən keçirin. Tez bir zamanda kömək almaq üçün Reactiflux Chat xüsusilə əla bir yoldur. Əgər cavab almırsınızsa və ya yenə də ilişmisinizsə, xahiş olunur bizə Github üzərindən Issue göndərin və biz sizə kömək etməyə cəhd edəcəyik.

İcmal

Quraşdırmanı artıq tamamladığınıza görə, gəlin indi React haqqında məlumat alaq!

React Nədir?

React - istifadəçi interfeysləri (UI) yaratmaq üçün deklarativ, səmərəli və elastik Javascript kitabxanasıdır. Bu kitabxana sizə “komponent” adlanan kiçik sərbəst kod hissələrindən mürəkkəb UI yaratmağa icazə verir.

React-də bir neçə növ komponent var, amma biz React.Component alt siniflərindən başlayacağıq:

class ShoppingList extends React.Component {
  render() {
    return (
      <div className="shopping-list">
        <h1>{this.props.name} üçün alış-veriş siyahısı</h1>
        <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
        </ul>
      </div>
    );
  }
}

// İşlətmə nümunəsi: <ShoppingList name="Rufat" />

Biz bu maraqlı XML-a oxşar təqlərdən, sonra danışacağıq. Biz komponentlərdən istifadə edərək React-ə ekranda nə görmək istədiyimizi deyirik. Məlumat dəyişəndə React səmərəli şəkildə komponentləri yeniləyir və yenidən render edir.

Yuxarıdakı nümunədə, ShoppingList bir React komponent sinifi və ya React komponent növüdür. Komponent props (“properties” sözünün qısa yazılışı) adında parametrlər qəbul edir və görünüş üçün iyerarxiyalı formada render funksiyasından qaytarır.

render funksiyası görmək istədiyiniz məzmunu qaytarır. React bu məzmun əsasında nəticəni göstərir. Xüsusi ilə, render React elementi (nə render edildiyinin yüngül məzmununu) qaytarır. Əksər React proqramçıları “JSX” adında xüsusi sintaksis işlədir. Bu sintaksis belə iyerarxiyalı strukturların yazılışını asanlaşdırır. <div /> sintaksisi qurulma zamanı React.createElement('div')-ə çevrilir. Yuxarıdakı nümunənin JSX-siz forması aşağıdakı kimi yazılır:

return React.createElement('div', {className: 'shopping-list'},
  React.createElement('h1', /* ... h1 children ... */),
  React.createElement('ul', /* ... ul children ... */)
);

Genişləndirilmiş versiyanı izləyin.

Əgər sizə maraqlıdırsa, createElement() API arayışında daha detallı izah edilir. Biz bu dərslikdə createElement() əvəzinə JSX-dən istifadə etməyə davam edəcəyik.

JSX, Javascriptin bütün gücü ilə gəlir. Siz hər hansı Javascript ifadəsini JSX-də fiqurlu mötərizənin içərisindən çağıra bilərsiniz. Hər React elementi bir sadə Javascript obyektidir. Siz bu obyekti dəyişənə təyin edə bilər və ya applikasiyanızda funksiyalara və s. göndərə bilərsiniz.

Yuxarıdakı nümunədə, ShoppingList komponenti yalnız hazır qurulmuş DOM komponentlərini (<div /><li />) render edir. Lakin, siz həmçinin öz komponentlərinizi də yarada və render edə bilərsiniz. Məsələn, siz yuxarıdakı alqı satqı siyahısını <ShoppingList /> kimi işlədə bilərsiniz. Hər React komponenti inkapsulasiya olunub deyə siz bu komponentləri bir-birindən asılı olmayacaq şəkildə istifadə edə bilərsiniz. Bu xüsusiyyət sadə komponentlərdən mürəkkəb UI-lar yaratmağa icazə verir.

Başlanğıc Kodunu Yoxlamaq

Əgər siz bu dərsliyi brauzerdə edəcəksinizsə bu kodu yeni təbdə açın: Başlanğıc Kodu. Əgər siz bu dərsliyi lokalda edəcəksinizsə, layihə direktoriyasında olan src/index.js faylını açın (siz bu fayla qurulma bölməsində əl gəzdirmisiniz).

Başlağıc Kodu sizin yazacağınız üçün baza kodudur. Biz CSS stilləri təmin etmişik ki, siz diqqətinizi yalnız React-i öyrənməyə və “X O oyunu” yazmağa yönləndirəsiniz.

Koda yaxından baxdığınızda, üç React komponentin olacağını görəcəksiniz:

  • Square
  • Board
  • Game

Square komponent tək bir <button>-u və Board komponenti 9 ədəd kvadrat render edir. Game komponenti Board-u elə-belə dəyərlər ilə render edir. Biz bu dəyərləri gələcək bölmələrdə dəyişəcəyik. İndiki zamanda bu kodda interaktiv komponent yoxdur.

Məlumatı Proplar ilə Göndərmək

Gəlin Board komponentindən Square komponentinə məlumat göndərək.

Dərslikdə irəliləyərkən kodları copy-paste etmək normaldır amma biz kodları əl ilə yazmağınızı tövsiyə edirik. Bu sizə əzələ yaddaşı yaratmağa və daha güclü anlamağa kömək edəcək.

Board-un renderSquare funksiyasında, Square-ə value propu əlavə edin:

class Board extends React.Component {
  renderSquare(i) {
    return <Square value={i} />;  }
}

Square-in render funksiyasında məlumatı göstərmək üçün {/* TODO */} kodunu {this.props.value} ilə əvəz edin:

class Square extends React.Component {
  render() {
    return (
      <button className="square">
        {this.props.value}      </button>
    );
  }
}

Öncə:

React Devtools

Sonra: Siz kvadratlarda rəqəmləri görməlisiniz.

React Devtools

Bu nöqtəyə kimi olan bütün kod

Təbriklər! Siz valideyn olan Board komponentindən “propu” uşaq olan Square komponentinə göndərdiniz. React-də məlumat axınını propların göndərilməsi ilə baş verir — valideyndən uşağa doğru.

İnteraktiv Komponent Yaratmaq

Gəlin, Square komponentinə tıklandığı zaman kvadratı “X” ilə dolduraq.

İlk öncə Square komponentindəki render() funksiyasında olan “button” təqini aşağıdakı koda çevirin:

class Square extends React.Component {
  render() {
    return (
      <button className="square" onClick={function() { console.log('click'); }}>        {this.props.value}
      </button>
    );
  }
}

Əgər indi siz Square-ə tısinfinianız, brauzerdə “click” yazısı ilə xəbərdarlıq alacaqsınız.

Qeyd

Yazını qısaltmaq və this-in baş qarışdırıcı işləmə prinsipinindən kənar olmaq üçün, biz buradakı və sonrakı hadisə işləyicilərində arrow funksiyaları sintaksisindən istifadə edəcəyik:

class Square extends React.Component {
 render() {
   return (
     <button className="square" onClick={() => console.log('click')}>       {this.props.value}
     </button>
   );
 }
}

Diqqət edin ki, biz onClick propuna funksiya göndəririk: onClick={() => alert('click')}. React, bu xəbərdarlığın yalnız düymənin klikləndiyi vaxt çıxmasını təmin edir. () =>-nu yaddan çıxarıb birbaşa onClick={alert('click')} yazmaq tez-tez edilən bir səhvlərdən biridir. Səhv formada yazdıqda, komponentin hər renderindən sonra (məsələn, dəyişiklikdən sonra baş verən yenidən render) xəbərdarlıq göstəriləcək (tıklamaqdan asılı olmayaraq).

Sıradakı addımda, biz Square komponentinin tıklandığını yadda saxlamaq və “X” işarəsi ilə doldurmaq istəyirik. Komponentlər “yadda saxlamaq” üçün state-dən istifadə edirlər.

React komponentlərinin state-ləri olması üçün sinif konstrukturunda this.state-dən istifadə edilir. this.state yalnız onu müəyyənləşdirən React komponentinə privat olmalıdır. Gəlin Square-in cari dəyərini this.state-də saxlayaq və Square tıklanarkən bu dəyəri dəyişək.

İlk olaraq state-i inisializasiya etmək üçün sinifə konstruktor əlavə edək:

class Square extends React.Component {
  constructor(props) {    super(props);    this.state = {      value: null,    };  }
  render() {
    return (
      <button className="square" onClick={() => console.log('click')}>
        {this.props.value}
      </button>
    );
  }
}

Qeyd

JavaScript siniflərində, alt sinifinin konstrukturunu tətbiq etdikdə mütləq super-dən istifadə etmək lazımdır. Konstrukturu olan bütün React komponentləri super(props) funksiya çağırışı ilə başlamalıdır.

Biz Square komponentinin cari state-inin dəyərini tıklamadan sonra göstərmək üçün, Square-in render funksiyasından istifadə edəcəyik:

  • <button> təqində this.props.value-nu this.state.value ilə əvəz et.
  • onClick={...} hadisə işləyicisini onClick={() => this.setState({value: 'X'})} ilə əvəz et.
  • Oxunaqlığı artırmaq üçün classNameonClick proplarını ayrı sətrlərdə yazın.

Bu dəyişikliklərdən sonra Square-in render funksiyasından qaytarılan <button> təqi aşağıdakı koda oxşamalıdır:

class Square extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: null,
    };
  }

  render() {
    return (
      <button
        className="square"        onClick={() => this.setState({value: 'X'})}      >
        {this.state.value}      </button>
    );
  }
}

Square komponentinin render funksiyasında olan onClick işləyicisində this.setState çağırmaq ilə biz React-ə <button> tıklananda komponenti yenidən render etməyi deyirik. Yeniləmədən sonra, Square-in this.state.value dəyəri 'X' olacaq və bu səbəbdən biz oyun taxtasında X görəcəyik. Hər hansı bir Square-i tıklayanda, X görünəcək.

Komponentdən setState çağırdıqda, React avtomatik olaraq komponentin içinsəki uşaq komponentləridə yeniləyir.

Bu nöqtəyə kimi olan bütün kod

Developer Alətləri

ChromeFirefox üçün olan React DevTools Genişlənməsi, React komponent iyerarxiyasını brauzerin developer alətlərindən gözdən keçirməyə imkan yaradır.

React Devtools

React DevTools sizə React komponentlərinin state və proplarına baxmağa icazə verir.

React DevTools-u qurduqdan sonra, siz səhifədə hər hansi bir elementə sağ düymə ilə tıklayıb, “Inspect”-ə tıklayıb developer alətlərini açın. Ən sağda yerləşən iki təb React təbləridir (“⚛️ Components” və “⚛️ Profiler”). Komponent ağacını yoxlamaq üçün “⚛️ Components” təbindən istifadə edin.

Nəzərə alın ki, CodePen-də işləməsi üçün əlavə bir neçə addım atmaq lazımdır:

  1. Login edin ve ya qeydiyyatdan keçib emailınızı təsdiq edin (spamın qarşısını almaq üçün lazımdır).
  2. “Fork” düyməsini tıklayın.
  3. “Change View” düyməsini tıklayıb “Debug mode” seçin.
  4. Yeni açılan təbdə brauzerin developer alətlərində React təbi olacaq.

Oyunu Tamamlamaq

Indi bizdə “X O oyununun” əsas blokları hazırdı. Oyunu tamamlamaq üçün biz “X” və “O”nun sıra ilə yerləşdirilməsini düzəltməli və oyunun sonunda qalibi müəyyənləşdirməliyik.

State-i Qaldırmaq

İndi, hər Square komponenti oyunun vəziyyətini saxlayır. Qalibi təyin etmək üçün biz bu 9 kvadratın dəyərini bir yerdə saxlamalıyıq.

Biz Board komponentinin hər Square komponentdən Square-in state-ini istəməsini fikirləşə bilərik. Bunun React-da mümkün olmasına baxmayaraq, biz bu yol ilə getməyi tövsiyə etmirik. Çünki belə üsulla yazılan kodu başa düşmək və refaktor etmək çətinləşir və baqların şansı artır. Bunu duzəltməyin ən yaxşı yolu oyunun state-ini hissə-hissə hər Square komponentdə saxlamaq əvəzinə bütünlüklə Board komponentində saxlamaqdır. Board komponenti hər Square-ə nə göstərəcəyini, Square-ə rəqəm göndərdiyimiz kimi, prop ilə göndərə bilər.

Çoxlu uşaqdan məlumat yığması və ya iki uşaq komponentin bir-biri ilə kommunikasiya etməsi üçün paylaşılan state-i valideyn komponentində bəyan edin. Valideyn komponent bu state-i uşaq komponentlərə proplar vasisəti ilə göndərə bilər. Bu metod uşaq komponentlər və valideyn komponentlər arasındakı məlumatları sinxron saxlayır.

React komponentlərini refaktor etdikdə, state-i valideyn komponentə qaldırmaq çox işlənən praktikadır. Gəlin bu metodu sınayaq.

Board komponentinə konstruktor əlavə edərək Board-un başlanğıc state-inə 9 ədəd null olan massiv əlavə edin. Bu 9 ədəd null 9 ədəd kvadrat üçündür:

class Board extends React.Component {
  constructor(props) {    super(props);    this.state = {      squares: Array(9).fill(null),    };  }
  renderSquare(i) {
    return <Square value={i} />;
  }

Kvadratları doldurduqda, oyun taxtasının vəziyyəti (this.state.squares massivi) belə formada olacaq:

[
  'O', null, 'X',
  'X', 'X', 'O',
  'O', null, null,
]

Board-un renderSquare funksiyası indi belə görsənir:

  renderSquare(i) {
    return <Square value={i} />;
  }

Bu dərsliyi başlayanda biz hər Square-də 0-dan 8-ə kimi rəqəmləri göstərmək üçün Board-dan value propunu göndərdik. Əvvəlki başqa bir bölmədə isə biz rəqəmləri Square-in daxili state-indən istifadə edərək “X” ilə əvəz etdik. Bu səbəbdən Square komponenti Board-dan göndərilən value propunu saymır.

Biz yenidən propu göndərmək mexanizmindən istifadə edəcəyik. Board komponentindən hər Square-ə kvadratın dəyərini göndərəcəyik ('X', 'O', və ya null). Biz artıq squares massivini Board-un konstrukturunda yaratmışıq və indi biz renderSquare funksiyasından bu massivi oxuyacağıq:

  renderSquare(i) {
    return <Square value={this.state.squares[i]} />;  }

Bu nöqtəyə kimi olan bütün kod

İndi hər Square value propundan bu dəyərlərdən birini alacaq: 'X', 'O', və ya null (boş kvadrat).

İndi biz Square tıklananda nə olacağını dəyişməliyik. Board komponenti kvadratların dəyərlərini özündə saxlayır. Bu səbəbdən biz Square komponentinə Board-un state-ini dəyişməsini bildirməliyik. State, yarandığı komponentə (bu misalda Board komponentinə) privat olduğundan, biz Square komponentindən Board-un state-ini dəyişə bilmirik.

Bunun əvəzinə, biz Board-dan Sqquare-ə funksiya göndərəcəyik. Bu funksiya Square tıklananda çağrılacaq. Board-un renderSquare funksiyasını aşağıdakına dəyişəcəyik:

  renderSquare(i) {
    return (
      <Square
        value={this.state.squares[i]}
        onClick={() => this.handleClick(i)}      />
    );
  }

Qeyd

Biz oxumanı artırmaq üçün qaytarılan elementi bir neçə sətrdə yazdıq və Javascriptin return-dən sonra nöqtə vergül qoyub kodumuzu sındırmaması üçün qaytarılan komponenti mötərizəyə saldıq.

İndi biz Board-dan Square-ə iki prop göndəririk: valueonClick. onClick propu funksiyadır və Square tıklananda çağrılacaq. Square komponentinə aşağıdakı dəyişiklikləri edəcəyik:

  • Square-in render funksiyasında this.state.value-nu this.props.value ilə əvəz edəcəyik
  • Square-in render funksiyasında this.setState()-i this.props.onClick() ilə əvəz edəcəyik
  • Square state saxlamadığından constructor-u Square-dən siləcəyik

Bu dəyişikliklərdən sonra Square komponenti belə olacaq:

class Square extends React.Component {  render() {    return (
      <button
        className="square"
        onClick={() => this.props.onClick()}      >
        {this.props.value}      </button>
    );
  }
}

Square tıklandıqda Board-un təmin etdiyi onClick funksiyası çağrılır. Bu prosesin icmalı:

  1. DOM qurulan <button> komponentinin onClick propu React-ə tıklama hadisəsi üçün hadisə qəbuledicisi qurmasını deyir.
  2. Düymə tıklandığı zaman, React, Square-in render() funksiyasında olan onClick hadisə işləyicisini çağırır.
  3. Bu hadisə işləyicisi this.props.onClick() funksiyasını çağırır. Square-in onClick propu Board tərəfindən müəyyənləşdirilib.
  4. Board-un Square-ə onClick={() => this.handleClick(i)} göndərməsindən, Square tıklandığında this.handleClick(i) funksiyasını çağırır.
  5. Biz handleClick() funksiyasını hələ tətbiq etmədiyimizdən bizim kod çökür. Əgər siz indi düyməni tıklasanız, sizdə qırmızı rəngdə xəta səhifəsi “this.handleClick is not a function” formasında çıxacaq.

Qeyd

DOM <button> elementinin qurulmuş komponent olduğundan, bu elementin onClick atributunun React-ə xüsusi mənası var. Square kimi xüsusi komponentlər üçün bu atributu adlandırmaq sizdən asılıdır. Biz Square-in onClick propunu və ya Board-un handleClick funksiyasını başqa cür də adlandıra bilərik və kod eyni formada işləyəcək. React-də hadisələr üçün on[Event] və hadisə işləyiciləri üçün handle[Event] adları işlətmək adətdir.

Square-i tıkladıqda, handleClick funksiyasının olmadığından bizə xəta gələcək. Gəlin indi bu funksiyanı Board sinfində tətbiq edək:

class Board extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null),
    };
  }

  handleClick(i) {    const squares = this.state.squares.slice();    squares[i] = 'X';    this.setState({squares: squares});  }
  renderSquare(i) {
    return (
      <Square
        value={this.state.squares[i]}
        onClick={() => this.handleClick(i)}
      />
    );
  }

  render() {
    const status = 'Sonrakı oyunçu: X';

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

Bu nöqtəyə kimi olan bütün kod

Bu dəyişikliklərdən sonra biz Square-ə tıklayaraq oyun taxtasını doldura bilirik. Amma indi oyunun vəziyyəti Square komponentlərdə individual yerləşmək əvəzinə Board komponentində saxlanır. Board-un state-i dəyişdikdə bütün komponentlər avtomatik yenidən render olunurlar. Bütün kvadratların vəziyyətlərini Board komponentində saxlamaq ilə biz gələcəkdə oyunun qalibini də müəyyənləşdirə biləcəyik.

Square komponentində state olmadığından, bütün Square komponentləri Board komponentindən dəyərləri alir və tıklandığı zaman Board komponentinə xəbər verir. React terminalogiyasında, Square komponentləri kontrol olunan komponentlər adlanırlar. Board komponentinin bu komponentlər üzərində tam kontrolu var.

Qeydə alin ki, biz handleClick funksiyasında mövcud squares massivini dəyişmək əvəzinə .slice() funksiyasını çağırıb massivin kopiyasını düzəldirik. Gələn bölmədə biz squares-in kopiyasını çıxarmağın səbəbini izah edəcəyik.

Dəyişməzlik Niyə Vacibdir

Əvvəli kod nümunəsində biz mövcud massivi dəyişmək əvəzinə .slice() operatoru ilə squares massivinin kopiyasını çıxarmağı tövsiyə etdik. Bu bölmədə biz dəyişməzlik (immutability) və dəyişməzliyi öyrənməyin niyə vacib olduğunu müzakirə edəcəyik.

Normalda məlumatı iki yol ilə dəyişmək olur. Birinci yanaşma məlumatın dəyərlərini birbaşa dəyişməklə məlumatı mutasiya etməkdir. İkinci yanaşma isə mövcud məlumatı dəyişikliklər olan kopiyası ilə əvəz etməkdir.

Məlumatı Mutasiya ilə Dəyişmək

var player = {score: 1, name: 'Orxan'};
player.score = 2;
// player {score: 2, name: 'Orxan'} bərabərdir

Məlumatı Mutasiyasız Dəyişmək

var player = {score: 1, name: 'Orxan'};

var newPlayer = Object.assign({}, player, {score: 2});
// player dəyişməyib, amma newPlayer {score: 2, name: 'Orxan'} bərabərdir

// Əgər siz obyekt yayma sintaksisindən istifadə edirsinizsə, siz belə yaza bilərsiniz:
// var newPlayer = {...player, score: 2};

Son nəticənin eyni olmasına baxmayaraq məlumatı mutasiya etmədən dəyişdikdə aşağıdakı faydalardan yararlanırıq.

Kompleks Xüsusiyyətlər Sadələşir

Dəyişməzlik kompleks xüsusiyyətlərin tətbiqini çox asanlaşdırır. Bu dərslikdə biz “X O oyununun” tarixçəsinin icmalını və əvvəlki gedişlərə qayıda bilmək üçün “zaman səyahəti” tətbiq edəcəyik. Bu xüsusiyyət yalnız oyunlara xas deyil. Bir çox applikasiyaların tələblərində hərəkətləri geri qaytarmaq və ya qabağa çəkmək olur. Məlumatın birbaşa dəyişməyindən çəkinməklə biz oyunun tarixçəsini saxlaya bilir və saxladığımız tarixçədən istifadə edə bilərik.

Dəyişikliklərin Aşkar Edilməsi

Mutasiya olunmuş obyektlərin birbaşa dəyişildiyindən, bu dəyişikliklərin aşkar edilməsi çətindir. Dəyişikliyi tapmaq üçün mutasiya olunmuş obyekt, əvvəlki kopiyaları ilə müqayisə edilməlidir. Bu müqayisə üçün bütün obyekt iyerarxiyası yoxlanmalıdır.

Mutasiya olunmamış obyektlərdə dəyişiklikləri aşkar etmək xeyli dərəcədə asandır. Əgər dəyişməz obyektin referansı əvvəki obyektin referansından fərqlidirsə, bu obyekt dəyişmişdir.

React-də Yenidən Render Etməyin Aşkarlanması

Dəyişməzlik React-də saf komponentlərin düzəldilməsinə kömək edir. Mutasiya olunmamış məlumatlar ilə dəyişikliyin olmasını asan formada aşkar edərək komponentin yenidən render ediləcəyini müəyyən etmək mümkündür.

shouldComponentUpdate()təmiz komponentlər haqqında öyrənmək üçün Performansın Optimallaşdırılması səhifəsini oxuya bilərsiniz.

Funksional Komponentlər

Biz indi Square-i funskional komponentə çevirəcəyik.

React-də funskional komponentlər komponentlərin sadə formada yazılması üçündür. Bu komponentlərin state-i olmur; yalnız render funksiyası olur. React.Component-dən törənən sinif yazmaq əvəzinə, biz props qəbul edən və nəyi render etməyi qaytaran funksiya yaradırıq. Funksional komponentləri yazmaq sinif komponentləri yazmaqdan daha az yorucudur. Bir çox komponent belə formada yazıla bilər.

Square sinfini aşağıdakı funksiya ilə əvəz edin:

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}

Biz this.props-u işlətdiyimiz hər iki yerdə props ilə əvəz etdik.

Bu nöqtəyə kimi olan bütün kod

Qeyd

Biz Square-i funksional komponentə çevirdikdə, həmçinin onClick={() => this.props.onClick()} propunu daha qısa onClick={props.onClick}-a (Hər iki tərəfdəki mötərizələrin olmamasını qeyd edin) çevirdik.

Sıranı Gözləmək

Biz “X O oyununda” tam aydın olan qüsura düzəltməliyik: “O”-lar oyun taxtasında işarələnə bilmirlər.

Biz ilk gedişi “X” ilə qeyd edək. Bu ilkin dəyər üçün biz Board-un konstruktorunda başlanğıc state-i dəyişməliyik:

class Board extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null),
      xIsNext: true,    };
  }

Hər zaman oyunçu hərəkət etdikdə, xIsNext (bulin) kimin sonrakı addımı atdığını müəyyənləşdirmək üçün çevriləcək. xIsNext-in dəyərini çevirmək üçün gəlin handleClick funksiyasını yeniləyək:

  handleClick(i) {
    const squares = this.state.squares.slice();
    squares[i] = this.state.xIsNext ? 'X' : 'O';    this.setState({
      squares: squares,
      xIsNext: !this.state.xIsNext,    });
  }

Bu dəyişiklikdə, “X”-lər və “O”-lar sıralarını gözləyə bilərlər.

Əlavə olaraq Board-un render funksiyasındakı “status” mətnində kimin sonrakı addımı atacağını da göstərək:

  render() {
    const status = 'Sonrakı oyunçu: ' + (this.state.xIsNext ? 'X' : 'O');
    return (
      // kodun qalanı dəyişməyib

Bütün dəyişiklikləri etdikdən sonra Board komponenti belə görünəcək:

class Board extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: Array(9).fill(null),
      xIsNext: true,    };
  }

  handleClick(i) {
    const squares = this.state.squares.slice();    squares[i] = this.state.xIsNext ? 'X' : 'O';    this.setState({      squares: squares,      xIsNext: !this.state.xIsNext,    });  }

  renderSquare(i) {
    return (
      <Square
        value={this.state.squares[i]}
        onClick={() => this.handleClick(i)}
      />
    );
  }

  render() {
    const status = 'Sonrakı oyunçu: ' + (this.state.xIsNext ? 'X' : 'O');
    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

Bu nöqtəyə kimi olan bütün kod

Qalibi Bəyan Etmək

Biz indi kimin sonrakı addımı atacağını bildiyimizdən, gəlin oyunun qalibini və ya oyunda heç bir gedişin qalmadığını göstərək. Qalibi tapmaq üçün aşağıdakı köməkçi funksiyanı faylın sonuna əlavə edin:

function calculateWinner(squares) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}

9 kvadratdan ibarət olan massiv verildikdə, bu funksiya qalibi müəyyənləşdirib, “X”, “O” və ya “null” qaytaracaq.

Biz Board-un render funksiyasında oyunçunun qazandığını yoxlamaq üçün calculateWinner(squares) funksiyasını çağıracağıq. Əgər oyunçu qalib olubsa, biz “Qalib: X” və ya “Qalib: O” formalı mətn göstərəcəyik. Board-un render funksiyasındakı status-u aşağıdakı formada dəyişəcəyik:

  render() {
    const winner = calculateWinner(this.state.squares);    let status;    if (winner) {      status = 'Qalib: ' + winner;    } else {      status = 'Sonrakı oyunçu: ' + (this.state.xIsNext ? 'X' : 'O');    }
    return (
      // kodun qalanı dəyişməyib

İndi Board-un handleClick funksiyasını dəyişərək kvadratın boş olmadığında və ya kiminsə qalib olduğunda funksiyadan tez qaytarıb tıklamanı iqnor edək:

  handleClick(i) {
    const squares = this.state.squares.slice();
    if (calculateWinner(squares) || squares[i]) {      return;    }    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      squares: squares,
      xIsNext: !this.state.xIsNext,
    });
  }

Bu nöqtəyə kimi olan bütün kod

Təbriklər! Sizdə indi işləyən “X O oyunu” var. Həmçinin siz React-in əsaslarını öyrəndiniz. Bu səbəbdən siz yəqinki əsl qalibsiniz.

Zaman Səyahətini Əlavə Etmək

Son bir tapşırıq kimi, gəlin əvvəlki gedişlərə qayıtmağı tətbiq edək.

Hərəkətlər Tarixcəsini Yadda Saxlamaq

Əgər biz squares massivini mutasiya etsə idik, zaman səyahətinin tətbiqi çətin olacaqdı.

Lakin, biz hər gedişdən sonra slide() funksiyasından istifadə edərək, squares massivinin kopiyasını çıxardıq və bu massivə mutasiya olunmayan kimi davrandıq. Bu bizə squares massivinin bütün köhnə versiyalarını saxlamağa və bu versiyalar arasında naviqasiya etməyə imkan yaradır.

Biz köhnə squares massivlərini history adında bir massivdə saxlayacağıq. history massivi, birinci gedişdən axırıncı gedişə kimi olan oyun taxtasının bütün vəziyyətlərinin təsviridir. Bu massiv aşağıdakı struktura malikdir:

history = [
  // Birinci gedişdən əvvəl
  {
    squares: [
      null, null, null,
      null, null, null,
      null, null, null,
    ]
  },
  // Birinci gedişdən sonra
  {
    squares: [
      null, null, null,
      null, 'X', null,
      null, null, null,
    ]
  },
  // İkinci gedişdən sonra
  {
    squares: [
      null, null, null,
      null, 'X', null,
      null, null, 'O',
    ]
  },
  // ...
]

İndi biz hansı komponentin bu history state-ini saxlayacağına qərar verməliyik.

State-i Yenidən Qaldırmaq

Biz ən yuxarıda olan Game komponentinin bütün keçmiş hərəkətlərinin siyahısını göstərməsini istəyirik. Bunun üçün bu komponent history-ə müraciət edə bilməlidir. Bu səbəbdən biz history state-ini Game komponentində saxlayacağıq.

history state-inin Game komponentində olması bizə squares state-inin bu komponentin uşağı olan Board komponentindən silinməsinə imkan yaradır. Əvvəl Square komponentindən Board komponentinə “state-i qaldırdığımız” kimi, biz indi state-i Board komponentindən Game komponentinə qaldırırıq. Bu Game komponentinin Board-un məlumatı üzərində tam kontrolu olmasını bildirir və Board-a history-də olan keçmiş gedişləri render etməyə imkan yaradır.

İlk olaraq, biz Game komponentinin konstruktorunda ilkin state-i inisializasiya edəcəyik:

class Game extends React.Component {
  constructor(props) {    super(props);    this.state = {      history: [{        squares: Array(9).fill(null),      }],      xIsNext: true,    };  }
  render() {
    return (
      <div className="game">
        <div className="game-board">
          <Board />
        </div>
        <div className="game-info">
          <div>{/* status */}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }
}

Sonra biz Board-a squaresonClick proplarını Game komponentindən almasını təmin edəcəyik. İndi Board-da bütün Square-lər üçün yalnız bir tıklamaq hadisə işləyicisi olduğundan, biz hansı Square-in tıklandığını bilmək üçün, hər Square-in yerini onClick işləyicisinə göndərməliyik. Aşağıda Board komponentini yeniləmək üçün olan addımlar göstərilib:

  • Board-dan constructor-u silin.
  • Board-un renderSquare funksiyasında, this.state.squares[i]-i this.props.squares[i] ilə əvəz edin.
  • Board-un renderSquare funksiyasında, this.handleClick(i)-i this.props.onClick(i) ilə əvəz edin.

Board komponenti aşağıdakı formada olacaq:

class Board extends React.Component {
  handleClick(i) {
    const squares = this.state.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      squares: squares,
      xIsNext: !this.state.xIsNext,
    });
  }

  renderSquare(i) {
    return (
      <Square
        value={this.props.squares[i]}        onClick={() => this.props.onClick(i)}      />
    );
  }

  render() {
    const winner = calculateWinner(this.state.squares);
    let status;
    if (winner) {
      status = 'Qalib: ' + winner;
    } else {
      status = 'Sonrakı oyunçu: ' + (this.state.xIsNext ? 'X' : 'O');
    }

    return (
      <div>
        <div className="status">{status}</div>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}

İndi biz Game komponentinin render funksiyasında, tarixçənin ən yeni qeydindən istifadə edərək, qalibi müəyyənləşdirib oyunun statusunu göstərəcəyik:

  render() {
    const history = this.state.history;    const current = history[history.length - 1];    const winner = calculateWinner(current.squares);    let status;    if (winner) {      status = 'Qalib: ' + winner;    } else {      status = 'Sonrakı oyunçu: ' + (this.state.xIsNext ? 'X' : 'O');    }
    return (
      <div className="game">
        <div className="game-board">
          <Board            squares={current.squares}            onClick={(i) => this.handleClick(i)}          />        </div>
        <div className="game-info">
          <div>{status}</div>          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }

Game komponentinin oyun statusunu göstərdiyindən, biz bu eyni kodu Board-un render funksiyasından silə bilərik. Refaktor etdikdən sonra, Board-un render funksiyası belə görünəcək:

  render() {    return (      <div>        <div className="board-row">          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }

Ən sonda, biz handleClick funksiyasını Board komponentindən Game komponentinə köçürməliyik. Həmçinin biz oyun komponentində state-in stukturunun fərqli olduğunu nəzərə alaraq handleClick-i dəyişməliyik. Game-in handleClick funksiyasında, biz yeni tarixçə qeydlərini history massivinə birləşdiririk.

  handleClick(i) {
    const history = this.state.history;    const current = history[history.length - 1];    const squares = current.squares.slice();    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      history: history.concat([{        squares: squares,      }]),      xIsNext: !this.state.xIsNext,
    });
  }

Qeyd

Bizim tanıdığımız massivdə olan push() funksiyasından fərqli olaraq, concat() funksiyası orijinal massivi mutasiya etmir. Bu səbəbdən biz concat-ə üstünlük veririk.

Bu nöqtədə, Board komponentinə yalnız renderSquarerender funkisyaları lazımdır. Oyunun vəziyyəti və handleClick funksiyası Game komponentində olmalıdır.

Bu nöqtəyə kimi olan bütün kod

Keçmiş Hərəkətləri Göstərmək

Biz “X O oyununun” tarixçəsini saxladığımızdan, biz indi gedişlərin siyahısını oyunçuya göstərə bilərik.

Biz React elementlərinin 1-ci sinif Javascript obyetkləri olduğunu və bu obyektərin applikasiyada fərqli yerlərə göndərilə bildiyini öyrəndik. Bir neçə elementi render etmək üçün, biz React elementlərinin massivindən istifadə edə bilərik.

JavaScriptdə massivlərdə məlumatları başqa məlumatlara uzlaşdırmaq üçün map() funksiyası var. Məsələn:

const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2); // [2, 4, 6]

map funksiyasından istifadə edərək, biz gedişlərin tarixçəsini ekranda düymələri göstərən React elementlərinə uzlaşdırıb, keçmiş gedişlərə “tullanmaq” üçün düymələr siyahısını göstərə bilərik.

Gəlin Game-in render funksiyasındahistory-nin üzərindənmap` edək:

  render() {
    const history = this.state.history;
    const current = history[history.length - 1];
    const winner = calculateWinner(current.squares);

    const moves = history.map((step, move) => {      const desc = move ?        'Gedişə get: #' + move :        'Oyunun əvvəlinə qayıt';      return (        <li>          <button onClick={() => this.jumpTo(move)}>{desc}</button>        </li>      );    });
    let status;
    if (winner) {
      status = 'Qalib: ' + winner;
    } else {
      status = 'Sonrakı oyunçu: ' + (this.state.xIsNext ? 'X' : 'O');
    }

    return (
      <div className="game">
        <div className="game-board">
          <Board
            squares={current.squares}
            onClick={(i) => this.handleClick(i)}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <ol>{moves}</ol>        </div>
      </div>
    );
  }

Bu nöqtəyə kimi olan bütün kod

“X O oyununun” tarixində olan bütün gedişlər üçün biz içərisində düymə <button> olan siyahı bəndi <li> yaradırıq. Düymədə this.jumpTo() adlanan funksiyanı çağıran onClick işləyicisi var. Biz hələki, jumpTo funksiyasını tətbiq etməmişik. İndi, biz oyunda baş vermiş bütün gedişlərin siyahısını və developer alətləri konsolunda aşağıdakı xəbərdarlığı görməliyik:

Xəbərdarlıq: Massiv və ya iteratorda hər bir uşağın unikal “key” propu olmalıdır. “Game” komponentinin render funksiyasına baxın.

Gəlin yuxarıdakı xəbərdarlığın nə olduğunu müzakirə edək.

Açarı Seçmək

Siyahı render etdikdə, React hər render olunmuş siyahı bəndi haqqında məlumat saxlayır. Siyahı yeniləndikdə React nəyin dəyişdiyini müəyyən etməlidir. Biz siyahı bəndlərinə yenisini əlavə edə, bəndi silə, bəndlərinin yerini dəyişə və ya list bəndləri yeniləyə bilərik.

Aşağıdakından

<li>Aydan: 7 tapşırıq qalıb</li>
<li>Elnur: 5 tapşırıq qalıb</li>

aşağıdakına

<li>Elnur: 9 tapşırıq qalıb</li>
<li>Aysel: 8 tapşırıq qalıb</li>
<li>Aydan: 5 tapşırıq qalıb</li>

keçidi fikirləşin.

Sayın çoxalmağından əlavə, bunu oxuyan insan, bizim Aydan ilə Elnurun sıralamasının dəyişdiyini, və Ayselin Aydan ilə Elnurun arasına əlavə edildiyini deyə bilər. Lakin, React bir kompyuter proqramı olduğundan bizim nə etmək istədiyimizi bilmir. React-in bizim niyyətimizi bilmədiyi üçün, biz hər siyahı bəndinə, qonşularından fərqləndirmək üçün, açar parametri ilə təyin etməliyik. Bir seçim aydan, elnur, və aysel sətirlərini açar kimi işlətməkdir. Əgər siz məlumatları kənar bir məlumat bazasından oxuyursunuzsa, Aydan, Elnur, və Ayselin məlumat bazasında olan IDlərini açar kimi işlətmək olar.

<li key={user.id}>{user.name}: {user.taskCount} tapşırıq qalıb</li>

Siyahı yenidən render etdikdə, React hər siyahı elementinin açarını götürür və keçmiş siyahıda eyni açarlı elementləri axtarır. Əgər cari siyahıda olan açar əvvəl mövcud deyildisə, React yeni komponent yaradır. Əgər keçmiş siyahıda olan açar cari siyahıda yoxdursa, React əvvəlki komponenti dağıdır. Əgər açarlar eynidirsə, açarı saxlayan element yerini dəyişir. Açarlar React-ə hər komponentin şəxsiyyəti haqqında məlumat verir və React-ə yenidən renderlər arası state-i saxlayır. Əgər komponentin açarı dəyişirsə, komponent dağılıb və yeni state ilə düzəlir.

key React-də xüsusi və reservlənmiş parametrdir (daha təkmil xüsusiyyət olan ref ilə birlikdə). Element yarandığı zaman React key parametrini çıxarır və bu açarı birbaşa qaytarılan elementdə saxlayır. key-in props-a aid olduğu görünsə belə, key, this.props.key ilə çağrıla bilməz. React avtomatik olaraq key-dən istifadə edərək hansı komponentin yenilənəcəyinə qərar verir. Komponent öz key-ini soruşa bilməz.

Dinamik siyahılar yaratdığınız zaman düzgün açarların təyin edilməsi ciddi şəkildə tövsiyə olunur. Əgər sizdə lazımi açar yoxdursa, sizin açar olana kimi məlumatınızın strukturunu dəyişməniz lazım ola bilər.

Əgər açar təyin edilməyibsə, React sizə xəbərdarlıq edib, massivin indeksini açar kimi işlədəcək. Massiv indeksini açar kimi işlətdikdə, siyahıdakı elementlərin yerini dəyişmək və ya elementləri əlavə edib/silmək problemli olur. Açıq-aydın key={i} göndərildikdə xəbərdarlıq susdurulur amma massiv indeksini göndərmək ilə olan problemlər yenədə qalır. Bu səbəbdən, bir çox halda belə formada xəbərdarlığı susdurmaq tövsiyə edilmir.

Açarlar qlobal formada unikal olmamalıdırlar. Yalnız komponentlər və qonşular arası unikal olmalıdırlar.

Zaman Səyahətini Tətbiq Etmək

“X O oyununun” tarixində, hər bir keçmiş gedişin unkial ID-si var: gedişin ardıcıllıq rəqəmi. Bu gedişlərin yeri heç vaxt dəyişmir, silinmir, və ya ortadan gediş əlavə olunmur. Bu səbəbdən, gedişin indeksini açar kimi işlətmək təhlükəsizdir.

Game komponentinin render funksiyasında, açarı <li key={move}> formada yaza bilərik. React-in açarlar haqqında olan xəbərdarlığı itəcək:

    const moves = history.map((step, move) => {
      const desc = move ?
        'Gedişə get #' + move :
        'Oyunun əvvəlinə qayıt';
      return (
        <li key={move}>          <button onClick={() => this.jumpTo(move)}>{desc}</button>
        </li>
      );
    });

Bu nöqtəyə kimi olan bütün kod

jumpTo funksiyasının olmadığından siyahıdakı hər hansı bir düyməni tıkladıqda xəta baş verir. jumpTo funksiyasını tətbiq etməmişdən qabaq, biz hansı gedişdə olduğumuzu bilmək üçün stepNumber state-ini Game komponentinə əlavə edəcəyik.

İlk olaraq, stepNumber: 0-ı Game constructor-da başlanğıc state-ə əlavə edin:

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      history: [{
        squares: Array(9).fill(null),
      }],
      stepNumber: 0,      xIsNext: true,
    };
  }

Sonra, biz stepNumber-i yeniləmək üçün Game komponentində jumpTo funksiyasını tətbiq edəcəyik. Əlavə olaraq, biz stepNumber cüt rəqəm olduqda xIsNext state-ini true edəcəyik:

  handleClick(i) {
    // Bu funksiya dəyişməyib
  }

  jumpTo(step) {    this.setState({      stepNumber: step,      xIsNext: (step % 2) === 0,    });  }
  render() {
    // Bu funksiya dəyişməyib
  }

Biz həmçinin Game-in handleClick funksiyasında kiçik dəyişikliklər edəcəyik.

Əlavə etdiyimiz stepNumber state-i istifadəçiyə gedişi göstərir. Yeni gediş etdikdə isə, biz stepNumber-i yeniləmək üçün stepNumber: history.length-nu this.setState funksiyasının arqumentinin bir hissəsi kimi əlavə etməliyik. Bu biz yeni gediş əlavə etdikdə eyni gedişi göstərməyin qabağını alır.

Biz həmçinin this.state.history-ni oxumağı this.state.history.slice(0, this.state.stepNumber + 1) ilə əvəz edəcəyik. Bu, biz keçmişdə olduğumuz zaman hərəkət etdikdə, olduğumuz nöqtədən yeni gedişin əlavə edilməsi və qalan “gələcək” gedişləri tarixinin silinməsini təmin edir.

  handleClick(i) {
    const history = this.state.history.slice(0, this.state.stepNumber + 1);    const current = history[history.length - 1];
    const squares = current.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      history: history.concat([{
        squares: squares
      }]),
      stepNumber: history.length,      xIsNext: !this.state.xIsNext,
    });
  }

Son olaraq, biz Game komponentinin render funksiyasında ən sonuncu gedişi göstərmək əvəzinə stepNumber indeksi ilə olan gedişi göstərməliyik:

  render() {
    const history = this.state.history;
    const current = history[this.state.stepNumber];    const winner = calculateWinner(current.squares);

    // kodun qalanı dəyişməyib

Biz oyun tarixində hər hansı bir addıma qayıtsaq, “X O oyununun” taxtası bizə o addımdan sonra oyunun vəziyyətini bizə göstərmək üçün yenilənəcək.

Bu nöqtəyə kimi olan bütün kod

Yekunlaşdırmaq

Təbriklər! Siz aşağıdakı xüsusiyyətləri olan “X O oyunu” yaratdınız:

  • Oyunu oynamağa icazə verir,
  • Oyunun qalibini müəyyənləşdirir,
  • Oyunu oynadığınız müddətdə bütün tarixini saxlayır,
  • Oyunçulara oyunun tarixinin icmalını və keçmiş versiyalarını görməyə imkan verir.

Əla! Arzulayırıq ki, siz də React-in işləməyi ilə bağlı fikir yarandı.

Son nəticəni buradan göndərə bilərsiniz: Son Nəticə.

Əgər sizdə əlavə vaxt varsa və ya yeni React bacarıqlarını möhkəmləndirmək istəyirsinizsə, aşağıda bu oyunu təkmilləşdirmək üçün bəzi ideayalar var (çətinliyin artırılması sırası ilə):

  1. Hər gedişin yerini gediş tarixi siyahısında (sütun, sıra) formatında göstərmək.
  2. Seçilmiş gediş siyahısını qalın şrift ilə göstər.
  3. Board-dakı kvadratları hardkod etmək əvəzinə iki sikl ilə yaradın.
  4. Gedişləri artan və ya azalan sıralama ilə sıralamaq üçün düymə əlavə edin.
  5. Əgər kimsə qalib olubsa, oyunu qazanan üç kvadratı vurğulayın.
  6. Əgər heç kəs qalib gəlmirsə, heç-heçə olduğunu göstərin.

Bu dərslik zamanı biz React-in konsepsiyalarını (elementlər, proplar, state, və komponentlər) nəzərdək keçirdik. Bu mövzular ilə bağlı daha ətraflı izahat üçün, qalan sənədləri oxuyun. Komponentlərin müəyyənləşdirilməsi haqqında daha ətraflı məlumat almaq üçün, React.Component API Arayışını oxuyun.

Is this page useful?Bu səhifəni redaktə edin