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

Tutoriål: Bevezetés a Reactbe

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.

Ez a tutoriål nem feltételez koråbbi React ismereteket.

MielƑtt elkezdjĂŒk a TutoriĂĄlt

Ebben a tutoriĂĄlban egy kis jĂĄtĂ©kot fogunk kĂ©szĂ­teni. CsĂĄbĂ­tĂł lehet ĂĄtugrani, mivel nem jĂĄtĂ©kokat kĂ©szĂ­tesz — de azĂ©rt adj neki egy esĂ©lyt. A technikĂĄk, amiket itt tanulsz, alapvetƑek bĂĄrmilyen React alkalmazĂĄshoz, Ă©s ha ezeket sikerĂŒl elsajĂĄtĂ­tanod, Ășgy sokkal jobban meg fogod Ă©rteni a React mƱködĂ©sĂ©t.

Tipp

Ez a tutoriĂĄl azoknak szĂłl, akik a gyakorlatias tanulĂĄst szeretik. Ha te az alap koncepciĂłk tanulĂĄsĂĄt preferĂĄlod, nĂ©zd meg a lĂ©pĂ©srƑl-lĂ©pĂ©sre ĂștmutatĂłnkat. ElkĂ©pzelhetƑ, hogy ezt a tutoriĂĄlt Ă©s az ĂștmutatĂłt egymĂĄs kiegĂ©szĂ­tĂ©sĂ©nek talĂĄlod majd.

Ez a tutoriĂĄl szekciĂłkra van felosztva:

Nem kell minden egyes szekciĂłt egyszerre befejezned, hogy eredmĂ©nyes lĂ©gy. PrĂłbĂĄlj meg olyan messzire eljutni, amennyire tudsz — akkor is, ha ez egy, vagy kĂ©t szekciĂł.

Mit kĂ©szĂ­tĂŒnk?

Ebben a tutoriålban megmutatjuk, hogy hogyan készíthetsz egy tic-tac-toe jåtékot Reactben.

Itt megnĂ©zheted, hogy mit is kĂ©szĂ­tĂŒnk: VĂ©geredmĂ©ny. Ha a kĂłdot nem teljesen Ă©rted, vagy ha nem vilĂĄgos a szintaxis, ne aggĂłdj! A tutoriĂĄl cĂ©lja, hogy megĂ©rtesse veled a Reactet, Ă©s annak szintaxisĂĄt.

AjĂĄnljuk, hogy mielƑtt folytatnĂĄd ezt a tutoriĂĄlt, olvass utĂĄna a tic-tac-toe jĂĄtĂ©knak. Az egyik funkciĂł amit Ă©szrevehetsz, az a jobb oldalon lĂ©vƑ szĂĄmozott lista a jĂĄtĂ©k tĂĄblĂĄjĂĄrĂłl. Ez a lista tartalmazza az összes korĂĄbbi lĂ©pĂ©st, ami a jĂĄtĂ©k menete sorĂĄn folyamatosan frissĂŒl.

Ha megismerkedtĂ©l a tic-tac-toe jĂĄtĂ©kkal, nyugodtan zĂĄrd be. Ez a tutoriĂĄl egy egyszerƱ sablont hasznĂĄl kiindulĂłpontkĂ©nt. A következƑ lĂ©pĂ©s felkĂ©szĂ­teni tĂ©ged, hogy elkezdhessĂŒk a jĂĄtĂ©k fejlesztĂ©sĂ©t.

ElƑfeltĂ©telek

FeltĂ©telezzĂŒk, hogy van mĂĄr valami tapasztalatod a HTML-el Ă©s JavaScripttel, de a tutoriĂĄl követĂ©se akkor sem lehet problĂ©ma, ha egy mĂĄsik programozĂłi nyelvbƑl jössz. TovĂĄbbĂĄ feltĂ©telezzĂŒk, hogy olyan programozĂłi koncepciĂłk, mint a fĂŒggvĂ©nyek, objektumok, tömbök Ă©s - egy bizonyos fokig - az osztĂĄlyok is ismertek szĂĄmodra.

Ha elƑször szeretnĂ©d ĂĄtnĂ©zni a JavaScriptet, akkor ezt az ĂștmutatĂłt ajĂĄnljuk. MegjegyzĂ©s: Ebben a tutoriĂĄlban pĂĄr ES6 (a JavaScript jelenlegi verziĂłja) funkciĂłt is hasznĂĄlunk, többek között nyĂ­lfunkciĂłkat, osztĂĄlyokat, let, Ă©s const utasĂ­tĂĄsokat. A Babel REPL segĂ­tsĂ©gĂ©vel leellenƑrĂ­zheted, hogy az ES6 mivĂ© lesz lefordĂ­tva.

BeĂĄllĂ­tĂĄsok a TutoriĂĄlhoz

Ezt a tutoriĂĄlt kĂ©tfĂ©lekĂ©ppen is elvĂ©gezheted: kĂłdolhatsz a böngĂ©szƑdbƑl, vagy felĂĄllĂ­thatsz egy helyi fejlesztƑi környezetet.

1. OpciĂł: kĂłdolj a böngĂ©szƑdben

A leggyorsabban Ă­gy ĂĄllhatsz neki!

ElƑször is nyisd meg a KezdƑ kĂłdot egy Ășj fĂŒlön. Az Ășj fĂŒl egy Ășj, ĂŒres tic-tac-toe tĂĄblĂĄt Ă©s a React kĂłdot kell, hogy mutassa. Ez a React kĂłd az, amit ebben a tutoriĂĄlban szerkeszteni fogunk.

Ugord åt a måsodik opciót, és a React åttekintéséhez menj az Áttekintés szekcióhoz.

2. OpciĂł: helyi fejlesztƑi környezet

Ez az opciĂł szabadon vĂĄlaszthatĂł, Ă©s nem kötelezƑ a tutoriĂĄl elvĂ©gzĂ©sĂ©hez!


VĂĄlaszthatĂł: InstrukciĂłk helyi környezetbƑl valĂł követĂ©shez, a kedvenc szövegszerkesztƑdhöz

Ahhoz, hogy követni tudd a tutoriĂĄlt egy ĂĄltalad vĂĄlasztott szerkesztƑbƑl, ez az opciĂł kicsivel több beĂĄllĂ­tĂĄst igĂ©nyel. Íme a lĂ©pĂ©sek:

  1. GyƑzƑdj meg róla, hogy a Node.js egy jelenlegi verziója telepítve van.
  2. Kövesd a Create React App telepĂ­tĂ©si ĂștmutatĂłjĂĄt egy Ășj projekt lĂ©trehozĂĄsĂĄhoz.
npx create-react-app my-app
  1. Törölj minden fĂĄjlt az Ășj projekt src/ mappĂĄjĂĄban.

Megjegyzés:

Ne töröld az egĂ©sz src mappĂĄt, csak az eredeti forrĂĄsfĂĄjlokat a mappĂĄban. A következƑ lĂ©pĂ©sben ki fogjuk cserĂ©lni az alap forrĂĄsfĂĄjlokat ebben a projektben.

cd my-app
cd src

# Ha Macet, vagy Linuxot hasznĂĄlsz:
rm -f *

# Vagy ha Windowson vagy:
del *

# Ezutån lépj vissza a projekt mappåba
cd ..
  1. Hozz létre egy index.css fåjlt a src/ mappåban, tartalma pedig legyen ez a CSS kód.
  2. Hozz létre egy index.js fåjlt a src/ mappåban, tartalma pedig legyen ez a JS kód.
  3. Add hozzĂĄ a következƑ hĂĄrom sort az index.js fĂĄjl tetejĂ©hez, a src/ mappĂĄban:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

Ha mindent jĂłl csinĂĄltĂĄl, Ă©s most lefuttatod az npm start parancsot a projekt mappĂĄjĂĄban, Ă©s megnyitod a http://localhost:3000-t a böngĂ©szƑben, egy ĂŒres tic-tac-toe mezƑt kell, hogy lĂĄss.

Szintaxis kiemelĂ©shez a következƑ instrukciĂłkat ajĂĄnljuk.

Segítség, elakadtam!

Ha bĂĄrmikor elakadsz, a közössĂ©gi tĂĄmogatĂĄsi forrĂĄsok segĂ­thet. KĂŒlönösen a Reactiflux Chat lehet hasznos, ha gyorsan szeretnĂ©l segĂ­tsĂ©get kapni. Ha nem Ă©rkezik vĂĄlasz, vagy mĂ©g mindig el vagy akadva, nyiss egy issue-t Ă©s segĂ­tĂŒnk.

Áttekintés

Most, hogy minden kĂ©szen ĂĄll, kezdjĂŒk a React ĂĄttekintĂ©sĂ©vel!

Mi az a React?

A React egy deklaratĂ­v, effektĂ­v, Ă©s rugalmas JavaScript könyvtĂĄr, felhasznĂĄlĂłi felĂŒletek kĂ©szĂ­tĂ©sĂ©hez. LehetƑvĂ© teszi komplex felhasznĂĄlĂłi felĂŒletek összeĂĄllĂ­tĂĄsĂĄt izolĂĄlt kĂłdrĂ©szletekbƑl, amiket “komponenseknek” hĂ­vunk.

A React rendelkezik egy pĂĄr komponenstĂ­pussal, de most kezdjĂŒk a React.Component alosztĂĄllyal:

class ShoppingList extends React.Component {
  render() {
    return (
      <div className="shopping-list">
        <h1>{this.props.name} bevĂĄsĂĄrlĂłlistĂĄja</h1>
        <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
        </ul>
      </div>
    );
  }
}

// Példa hasznålata: <ShoppingList name="Mark" />

NemsokĂĄra beszĂ©lĂŒnk a vicces XML-szerƱ cĂ­mkĂ©krƑl is. A komponensek segĂ­tsĂ©gĂ©vel mondjuk meg a Reactnek, hogy mit szeretnĂ©nk lĂĄtni a kĂ©pernyƑn. Ha az adatunk megvĂĄltozik, a React hatĂ©konyan frissĂ­ti Ă©s Ășjrarendereli a komponensĂŒnket.

Itt a ShoppingList egy React komponensosztĂĄly, vagy React komponenstĂ­pus. Egy komponens paramĂ©tereket fogad, amiket props-nak hĂ­vunk (angol “properties” rövidĂ­tĂ©se), Ă©s egy nĂ©zethierarchiĂĄt ad vissza, a render metĂłduson keresztĂŒl.

A render metĂłdus egy leĂ­rĂĄsĂĄt adja vissza annak, amit a kĂ©pernyƑn szeretnĂ©l lĂĄtni. A React fogja a leĂ­rĂĄst, Ă©s megjelenĂ­ti az eredmĂ©nyt. Pontosabban, a render metĂłdus egy React elem-et ad vissza, ami egy könnyƱsĂșlyĂș leĂ­rĂĄsa annak, amit renderelni kell. A legtöbb React fejlesztƑ egy speciĂĄlis szintaxist hasznĂĄl, ezt “JSX”-nek hĂ­vjĂĄk, ami könnyebbĂ© teszi ezen struktĂșrĂĄk Ă­rĂĄsĂĄt. A <div /> szintaxist React.createEelement('div')-tĂ© transzformĂĄljuk kompilĂĄlĂĄskor. A fenti pĂ©lda egyenĂ©rtĂ©kƱ az alĂĄbbival:

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

Nézd meg a teljes verziót.

Ha érdekel a createElement() részletesebb leíråsa, nézd meg az API referenciåt, de ebben a tutoriålban ezt nem fogjuk hasznålni. A JSX-et viszont igen.

A JSX rendelkezik a JavaScript minden erejĂ©vel. A JSX-be bĂĄrmilyen JavaScript kifejezĂ©st tehetsz, kapcsos zĂĄrĂłjelek közĂ©. Minden React elem egy JavaScript objektum, amit vĂĄltozĂłkban tĂĄrolhatsz, vagy körbekĂŒldhetsz a programodban.

A fenti ShoppingList komponens csak beĂ©pĂ­tett DOM komponenseket renderel, mint a <div /> Ă©s az <li />. De összeĂĄllĂ­thatsz Ă©s renderelhetsz egyedi React komponenseket is. PĂ©ldĂĄul a <ShoppingList /> Ă­rĂĄsĂĄval utalhatunk az egĂ©sz bevĂĄsĂĄrlĂłlistĂĄra. Minden React komponens elzĂĄrtan Ă©s fĂŒggetlenĂŒl operĂĄlhat; ez lehetƑvĂ© teszi szĂĄmodra komplex felhasznĂĄlĂłi kezelƑfelĂŒletek Ă©pĂ­tĂ©sĂ©t egyszerƱ komponensekbƑl.

KezdƑ kĂłd ellenƑrzĂ©se

Ha a tutoriĂĄlon a böngĂ©szƑdbƑl fogsz dolgozni, nyisd meg ezt a kĂłdot egy Ășj fĂŒlön: KezdƑ kĂłd. Ha helyi környezetben fogsz dolgozni, nyisd meg a src/index.js fĂĄjlt a projekt mappĂĄdban (mĂĄr korĂĄbban szerkesztetted a fĂĄjlt a beĂĄllĂ­tĂĄsok rĂ©szben).

Ez a KezdƑ kĂłd az alapja annak, amit kĂ©szĂ­tĂŒnk. A CSS stĂ­luslapot megadtuk, hogy csak a Reactre Ă©s a tic-tac-toe jĂĄtĂ©k programozĂĄsĂĄra kelljen fĂłkuszĂĄlnod.

A kĂłd tanulmĂĄnyozĂĄsĂĄval megĂĄllapĂ­thatod, hogy hĂĄromfĂ©le React komponensĂŒnk van:

  • Square
  • Board
  • Game

A Square komponens egy egyszerƱ <button>-t renderel, amĂ­g a Board 9 Square-t. A Game komponens egy jĂĄtĂ©ktĂĄblĂĄt renderel helyƑrzƑ Ă©rtĂ©kekkel, amiket kĂ©sƑbb mĂłdosĂ­tunk. Jelenleg nincs egyetlen interaktĂ­v komponens sem.

AdattovĂĄbbĂ­tĂĄs propokkal

Hogy vĂ©gre bemocskoljuk a kezĂŒnk, kĂŒldjĂŒnk adatot a Board komponensbƑl a Square komponensnek.

ErƑsen ajĂĄnljuk, hogy minden kĂłdot kĂ©zzel Ă­rj a tutoriĂĄl sorĂĄn, Ă©s ne hasznĂĄlj mĂĄsolĂĄs/beillesztĂ©st. Ez hozzĂĄ fog jĂĄrulni ahhoz, hogy jobban megĂ©rtsd mi is törtĂ©nik, Ă©s kĂ©sƑbb minden magĂĄtĂłl jön majd.

A Board renderSquare metĂłdusĂĄban vĂĄltoztasd meg a kĂłdot, hogy egy value propot tudj kĂŒldeni a Square komponensnek:

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

VĂĄltoztasd meg a Square komponens render metĂłdusĂĄt Ășgy, hogy ĂĄtĂ­rod a {/* TODO */} rĂ©szt {this.props.value}-ra:

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

ElƑtte:

React Devtools

Utåna: Egy szåmot kell låss minden négyzetben a renderelés sorån.

React Devtools

Nézd meg a teljes kódot ezen a ponton

GratulĂĄlunk! Sikeresen “lekĂŒldtĂ©l egy propot” egy szĂŒlƑ Board komponensbƑl egy gyermek Square komponensnek. React alkalmazĂĄsokban a propok lekĂŒldĂ©sĂ©vel tudsz informĂĄciĂłt mozgatni szĂŒlƑktƑl gyermek komponenseknek.

Készíts egy interaktív komponenst

TöltsĂŒk ki a Square komponenst egy “X”-szel, rĂĄkattintĂĄs esetĂ©n. ElƑször is vĂĄltoztasd meg a button cĂ­mkĂ©t a render() metĂłdus visszatĂ©rĂ©sĂ©ben erre:

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

Ha most kattintasz a Square-re, egy Ă©rtesĂ­tĂ©st kell lĂĄss a böngĂ©szƑdben.

Megjegyzés

A kevesebb gĂ©pelĂ©s Ă©s a this fĂ©lreĂ©rthetƑ viselkedĂ©sĂ©nek elkerĂŒlĂ©se Ă©rdekĂ©ben, innentƑl a nyĂ­lfunkciĂł szintaxist fogjuk hasznĂĄlni az esemĂ©nykezelƑkhöz:

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

Vedd Ă©szre, hogy az onClick={() => alert('kattintĂĄs')} segĂ­tsĂ©gĂ©vel egy fĂŒggvĂ©nyt kĂŒldĂŒnk le propkĂ©nt onClick nĂ©ven. A React csak kattintĂĄs utĂĄn fogja meghĂ­vni ezt a fĂŒggvĂ©nyt. Gyakori hiba csak ennyit Ă­rni onClick={alert('kattintĂĄs')}, Ă©s elfelejteni a () => rĂ©szt. Ez meghĂ­vnĂĄ a fĂŒggvĂ©nyt a komponens minden ĂșjrarenderelĂ©sĂ©nĂ©l.

KövetkezƑ lĂ©pĂ©skĂ©nt azt prĂłbĂĄljuk elĂ©rni, hogy a Square komponens “emlĂ©kezzen” arra, hogy rĂĄ lett kattintva, Ă©s töltse ki magĂĄt egy “X”-szel. Ahhoz, hogy komponensek “emlĂ©kezni” tudjanak, state-t (ĂĄllapotot) hasznĂĄlnak.

React komponensekben ĂĄllapotot a this.state segĂ­tsĂ©gĂ©vel deklarĂĄlhatunk a konstruktorban. A this.state ĂĄllapotra Ășgy kell tekintenĂŒnk, hogy az privĂĄt legyen abban az osztĂĄlyban, amiben az definiĂĄlva lett. TĂĄroljuk a Square jelenlegi Ă©rtĂ©kĂ©t a this.state objektumban, Ă©s vĂĄltoztassuk azt meg, ha a Square-re kattintunk.

ElƑször is adjunk hozzĂĄ egy konstruktort az osztĂĄlyhoz, hogy inicializĂĄljuk az ĂĄllapotot:

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

Megjegyzés

JavaScript osztĂĄlyokban mindig meg kell hĂ­vnod a super metĂłdust, amikor definiĂĄlod a konstruktort egy alosztĂĄlyban. Minden React komponensosztĂĄly, ami rendelkezik egy constructor-ral, egy super(props) hĂ­vĂĄssal kell, hogy kezdƑdjön.

Most pedig våltoztassuk meg a Square render metódusåt, hogy az ållapot jelenlegi értékét mutassa:

  • CserĂ©ld ki a this.props.value-t this.state.value-ra, a <button> cĂ­mkĂ©ben.
  • CserĂ©ld ki az onClick={...} esemĂ©nykezelƑt erre: onClick={() => this.setState({value: 'X'})}.
  • Tedd a className Ă©s onClick propokat kĂŒlön sorokba, a jobb olvashatĂłsĂĄg Ă©rdekĂ©ben.

Ezen våltoztatåsok utån a <button> címke, amit a Square render metódusa visszaad, így néz ki:

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

Amikor a Square komponens render metĂłdusĂĄban az onClick kezelƑ meghĂ­vja a this.setState metĂłdust, a React minden alkalommal Ășjra fogja renderelni a Square komponenst, amikor a <button> elemre rĂĄkattintunk. A frissĂ­tĂ©s utĂĄn a Square this.state.value Ă©rtĂ©ke 'X' lesz, tehĂĄt egy X-szet fogunk lĂĄtni a jĂĄtĂ©ktĂĄblĂĄn. Ha bĂĄrmelyik Square-re kattintasz, egy X kell hogy megjelenjen.

Amikor a setState metódust meghívjuk egy komponensben, a React automatikusan frissíti annak minden gyermek komponensét is.

Nézd meg a teljes kódot ezen a ponton

FejlesztƑi eszközök

A React Devtools fejlesztƑi eszközök kiegĂ©szĂ­tƑ Chrome-hoz Ă©s Firefox-hoz segĂ­t a React komponens fa vizsgĂĄlatĂĄban, a böngĂ©szƑd fejlesztƑi eszközeivel.

React Devtools

A React DevTools segĂ­t ellenƑrizni a React komponenseid propjait Ă©s ĂĄllapotait (state).

A React DevTools telepĂ­tĂ©se utĂĄn kattints bĂĄrmelyik elemre jobb egĂ©rgombbal az oldalon, majd kattints a “VizsgĂĄlat”-ra a fejlesztƑi eszközök megnyitĂĄsĂĄhoz. Ekkor a React fĂŒlek (“⚛ Components” Ă©s “⚛ Profiler”) megjelennek a jobb oldalon utolsĂł kĂ©t fĂŒlkĂ©nt. A komponensfa vizsgĂĄlatĂĄhoz hasznĂĄld a “⚛ Components” fĂŒlet.

Ha azonban a CodePen-en dolgozol, szĂŒksĂ©g lesz pĂĄr extra lĂ©pĂ©sre ahhoz, hogy egy mƱködjön:

  1. Jelentkezz be, vagy regisztrĂĄlj, Ă©s erƑsĂ­tsd meg az e-mailed (spam elkerĂŒlĂ©se Ă©rdekĂ©ben).
  2. Kattints a “Fork” gombra.
  3. Kattints a “Change View”-ra, majd válaszd a “Debug mode”-t.
  4. Az Ășj megnyĂ­lĂł fĂŒlön, a fejlesztƑi eszközökben lesz egy React fĂŒl.

Jåték befejezése

KĂ©sz vagyunk a tic-tac-toe jĂĄtĂ©k alap Ă©pĂ­tƑelemeivel. Egy teljes jĂĄtĂ©khoz azonban az “X”-szek Ă©s “O”-k elhelyezĂ©sĂ©nek a vĂĄltakozĂĄsĂĄra van szĂŒksĂ©gĂŒnk a jĂĄtĂ©ktĂĄblĂĄn, Ă©s szĂŒksĂ©gĂŒnk van egy mĂłdra, hogy megĂĄllapĂ­thassuk a gyƑztest.

Állapot felemelése

Jelenleg minden Square komponens kĂŒlön kezeli a jĂĄtĂ©k ĂĄllapotĂĄt. A gyƑztes ellenƑrzĂ©sĂ©hez mind a 9 nĂ©gyzet Ă©rtĂ©kĂ©t egy helyen fogjuk kezelni.

Azt gondolhatnĂĄnk, hogy a Board komponens csak egyszerƱen vĂ©gigkĂ©rdezi minden Square ĂĄllapotĂĄt. BĂĄr ez lehetsĂ©ges a Reactben, nem tĂĄmogatjuk, mert Ă­gy a kĂłd nehezen Ă©rthetƑvĂ© vĂĄlik, fogĂ©kony lesz hibĂĄkra, Ă©s nehĂ©z lesz ĂșjraĂ­rni. Ehelyett a legjobb mĂłdszer az, ha a jĂĄtĂ©k ĂĄllapotĂĄt a szĂŒlƑ Board komponensben tĂĄroljuk minden Square komponens helyett. A Board komponens meg tudja mondani minden Square komponensnek mit mutasson propok lekĂŒldĂ©sĂ©vel, ahogyan egy szĂĄmot is lekĂŒldtĂŒnk minden Square komponensnek

Több gyermekbƑl valĂł adatgyƱjtĂ©shez, vagy ahhoz, hogy kĂ©t gyermekkomponens kommunikĂĄlni tudjon egymĂĄssal, az ĂĄllapotot, amin a komponensek osztozni fognak, egy közös szĂŒlƑkomponensben kell, hogy deklarĂĄld. A szĂŒlƑkomponens ezutĂĄn le tudja kĂŒldeni a megosztott ĂĄllapotot a gyermekeknek propok segĂ­tsĂ©gĂ©vel; ez szinkronizĂĄlja a gyermekeket egymĂĄssal Ă©s a szĂŒlƑ komponensĂŒkkel.

A helyi ĂĄllapot felemelĂ©se egy szĂŒlƑ komponensbe gyakori tevĂ©kenysĂ©g, amikor React komponenseket Ă­runk Ășjra. — Ragadjuk meg az alkalmat, Ă©s prĂłbĂĄljuk ezt ki gyakorlatban.

Adj hozzĂĄ egy konstruktort a Board komponenshez, Ă©s ĂĄllĂ­tsd be a kezdƑ ĂĄllapotot, hogy az tartalmazzon egy tömböt 9 null Ă©rtĂ©kkel, amik a 9 nĂ©gyzetnek felelnek meg:

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

Amikor kĂ©sƑbb a tĂĄblĂĄt töltjĂŒk ki, a this.state.sqares tömb valahogy Ă­gy fog kinĂ©zni:

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

A Board komponens renderSquare metódusa jelenleg így néz ki:

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

Kezdetben, ahhoz szĂĄmokat mutassunk 0-tĂłl 8-ig minden Square komponensben, lekĂŒldtĂŒk a value propot a Board komponensbƑl. Egy mĂĄsik korĂĄbbi lĂ©pĂ©sben lecserĂ©ltĂŒk a szĂĄmokat “X” jelekre, amit a Square komponens sajĂĄt ĂĄllapota hatĂĄrozott meg. A Square komponens jelenleg ezĂ©rt nem veszi figyelembe a value prop Ă©rtĂ©kĂ©t, amit a Board komponens kĂŒld neki.

Most megint igĂ©nybe fogjuk venni a propkĂŒldĂ©si mechanizmust. Úgy mĂłdosĂ­tjuk a Board komponenst, hogy az Ă©rtesĂ­tsen minden Square komponenst annak jelenlegi Ă©rtĂ©kĂ©rƑl ('X', 'O', vagy null). A squares tömböt mĂĄr korĂĄbban definiĂĄltuk a Board komponens konstruktorĂĄban, Ă©s most Ășgy mĂłdosĂ­tjuk a Board komponens renderSquare metĂłdusĂĄt, hogy az ebbƑl a tömbbƑl olvasson:

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

Nézd meg a teljes kódot ezen a ponton

Így most minden Square komponens fogadni fog egy value propot, ami lehet 'X', 'O', vagy ĂŒres nĂ©gyzetek esetĂ©ben null.

A következƑben meg kell vĂĄltoztatnunk mi törtĂ©njen, ha a Square komponensre rĂĄkattintanak. Most mĂĄr a Board komponens kezeli melyik nĂ©gyzetek vannak kitöltve. Valahogy el kell Ă©rnĂŒnk, hogy a Square komponens frissĂ­tse a Board ĂĄllapotĂĄt. Mivel az ĂĄllapot privĂĄt arra a komponensre nĂ©zve amiben az definiĂĄlva van, a Board ĂĄllapota nem frissĂ­thetƑ közvetlenĂŒl a Square komponensbƑl.

Ehelyett lekĂŒldĂŒnk egy fĂŒggvĂ©nyt a Board komponensbƑl a Square-nek, Ă©s hagyjuk, hogy a Square meghĂ­vja ezt a fĂŒggvĂ©nyt, amikor egy nĂ©gyzetre kattintanak. Ehhez meg kell vĂĄltoztatnunk a renderSquare metĂłdust a Board komponensben:

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

Megjegyzés

Az olvashatĂłsĂĄg Ă©rdekĂ©ben a visszakĂŒldött elemet több sorba tördeljĂŒk, Ă©s hozzĂĄadunk zĂĄrĂłjeleket is, hogy a JavaScript ne adjon pontosvesszƑt a return utĂĄn, ezzel elrontva a kĂłdunkat.

Így mĂĄr kĂ©t propot kĂŒldĂŒnk le a Board-bĂłl a Square-nek: value Ă©s onClick. Az onClick prop egy fĂŒggvĂ©ny, amit a Square meg tud hĂ­vni, ha rĂĄkattintanak. A Square komponensen a következƑ vĂĄltozĂĄsokat eszközöljĂŒk:

  • CserĂ©ld le a this.state.value-t this.props.value-ra a Square render metĂłdusĂĄban
  • CserĂ©ld le a this.setState()-t this.props.onClick()-re a Square render metĂłdusĂĄban
  • Töröld a constructor-t a Square komponensbƑl, mivel az többĂ© nem követi a jĂĄtĂ©k ĂĄllĂĄsĂĄt

A Square komponens így néz ki a våltoztatåsok utån:

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

Amikor a Square komponensre rĂĄkattintanak, az onClick fĂŒggvĂ©ny meg lesz hĂ­vva, amit a Board komponens szolgĂĄltat. Íme egy összefoglalĂł, hogy ez hogyan is lehetsĂ©ges:

  1. Az onClick prop a beĂ©pĂ­tett DOM <button> komponensben közli a Reacttel, hogy ĂĄllĂ­tson fel egy esemĂ©nyfigyelƑt kattintĂĄsra.
  2. Amikor a gombra kattintanak, a React meghĂ­vja az onClick esemĂ©nyfigyelƑt, ami a Square komponens render() metĂłdusĂĄban definiĂĄlva lett.
  3. Ez az esemĂ©nyfigyelƑ meghĂ­vja a this.props.onClick() fĂŒggvĂ©nyt. A Square onClick propja a Board komponensben lett definiĂĄlva.
  4. Mivel a Board lekĂŒldte az onClick={() => this.handleClick(i)} propot a Square komponensnek, a Square meghĂ­vja a this.handleClick(i) fĂŒggvĂ©nyt, ha rĂĄkattintanak.
  5. Mivel a handleClick() metĂłdust mĂ©g nem definiĂĄltuk, a kĂłdunk összeomlik. Ha most kattintasz egy nĂ©gyzetre, egy piros hibĂĄt kell lĂĄtnod a kĂ©pernyƑn, ami valami olyat mond, hogy “this.handleClick is not a function”, azaz “a this.handleClick nem fĂŒggvĂ©ny”.

Megjegyzés

A <button> gombelem onClick attribĂștuma speciĂĄlis jelentĂ©ssel bĂ­r a React szĂĄmĂĄra, mivel ez egy beĂ©pĂ­tett komponens. Egy egyedi komponens esetĂ©n, mint pĂ©ldĂĄul a Square, a megnevezĂ©s tƑled fĂŒgg. A Square onClick propjĂĄnak mĂĄs nevet is adhatunk, vagy akĂĄr a Board handleClick metĂłdusĂĄnak, Ă©s a kĂłd ugyanĂșgy mƱködne. A Reactben, közös megĂĄllapodĂĄs alapjĂĄn on[Event]-nek hĂ­vjuk azokat a propokat, amik esemĂ©nyeket kĂ©pviselnek Ă©s handle[Event]-nek azokat a metĂłdusokat amik esemĂ©nyeket kezelnek.

Ha råkattintunk egy Square komponensre, egy hibåt kell hogy kapjunk, mivel a handleClick még nincs definiålva. Most hozzåadjuk a handleClick metódust a Board osztålyhoz:

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 = 'KövetkezƑ jĂĄtĂ©kos 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>
    );
  }
}

Nézd meg a teljes kódot ezen a ponton

Ezen vĂĄltoztatĂĄsok utĂĄn Ășjra rĂĄ tudunk kattintani a Square komponensekre, hogy kitöltsĂŒk Ƒket, ahogy eddig is. Azonban az ĂĄllapot most mĂĄr a Board komponensben van tĂĄrolva ahelyett, hogy minden Square kĂŒlön tĂĄrolnĂĄ a sajĂĄt ĂĄllapotĂĄt. Ha a Board ĂĄllapota megvĂĄltozik, a Square komponens automatikusan Ășjra fog renderelni. Minden nĂ©gyzet ĂĄllapotĂĄnak a Board komponensben valĂł tĂĄrolĂĄsa lehetƑvĂ© teszi, hogy a jövƑben megĂĄllapĂ­tsuk a gyƑztest.

Mivel a Square komponens többĂ© nem kezel ĂĄllapotot, a Square komponens most mĂĄr csak a Board komponenstƑl fogad Ă©rtĂ©keket, Ă©s Ă©rtesĂ­ti azt, ha rĂĄkattintanak. React nyelven ez azt jelenti, hogy a Square komponensek kontrollĂĄlt komponensek. A Board komponens teljes mĂ©rtĂ©kben irĂĄnyĂ­tja Ƒket.

Vedd Ă©szre, hogy a handleClick metĂłdusban meghĂ­vjuk a .slice() metĂłdust a tömbön, hogy a squares tömb egy mĂĄsolatĂĄt mĂłdosĂ­tsuk, ne az eredetit. A következƑ szekciĂłban elmagyarĂĄzzuk, hogy miĂ©rt kĂ©szĂ­tjĂŒk ezt a squares tömb mĂĄsolatot.

MegvĂĄltoztathatatlansĂĄg fontossĂĄga

Az elƑzƑ kĂłdpĂ©ldĂĄban azt tanĂĄcsoltuk, hogy a .slice() metĂłdussal kĂ©szĂ­tsĂŒnk egy squares tömb mĂĄsolatot, hogy ne az eredeti tömböt mĂłdosĂ­tsuk. Most megvitatjuk a megvĂĄltoztathatatlansĂĄgot, Ă©s hogy miĂ©rt fontos ennek megtanulĂĄsa.

AdatvĂĄltoztatĂĄsra kĂ©t ĂĄltalĂĄnos megközelĂ­tĂ©s lĂ©tezik. Az elsƑ megközelĂ­tĂ©s az, hogy közvetlenĂŒl megvĂĄltoztatjuk az adat Ă©rtĂ©kĂ©t. A mĂĄsodik megközelĂ­tĂ©s lecserĂ©lni az adatot egy mĂĄsolattal, ami tartalmazza a kĂ­vĂĄnt vĂĄltoztatĂĄsokat.

AdatvĂĄltozĂĄs mutĂĄciĂłval

var player = {score: 1, name: 'György'};
player.score = 2;
// A player most {score: 2, name: 'György'}

AdatvĂĄltozĂĄs mutĂĄciĂł nĂ©lkĂŒl

var player = {score: 1, name: 'György'};

var newPlayer = Object.assign({}, player, {score: 2});
// A player våltozatlan, a newPlayer pedig {score: 2, name: 'György'}

// Vagy ha hasznålod az objektum kiterítési szintaxisjavaslatot, ezt is írhatod:
// var newPlayer = {...player, score: 2};

A vĂ©geredmĂ©ny ugyanaz, de azzal, hogy nem közvetlenĂŒl vĂĄltoztatjuk meg az eredeti (vagy eredeti alĂĄ rendelt) adatot, hasznos lehet a lent leĂ­rt okokbĂłl.

Komplex tulajdonsågok vålnak egyszerƱvé

A megvĂĄltoztathatatlansĂĄggal bonyolult tulajdonsĂĄgok vĂĄlnak egyszerƱen implementĂĄlhatĂłvĂĄ. Ebben a tutoriĂĄlban kĂ©sƑbb implementĂĄlni fogunk egy “idƑutazó” funkciĂłt, ami lehetƑvĂ© teszi szĂĄmunkra a tic-tac-toe jĂĄtĂ©k lĂ©pĂ©störtĂ©netĂ©nek tanulmĂĄnyozĂĄsĂĄt, Ă©s abban valĂł korĂĄbbi lĂ©pĂ©sre valĂł “visszaugrĂĄst”. Ez a funkciĂł nem egyedi jĂĄtĂ©kokra — a visszavonĂĄsi kĂ©pessĂ©g, vagy ĂșjracsinĂĄlni bizonyos tevĂ©kenysĂ©geket, egy gyakori követelmĂ©ny alkalmazĂĄsokban. A közvetlen adatvĂĄltozĂĄs elkerĂŒlĂ©sĂ©vel Ă©rintetlenĂŒl tudjuk tartani a jĂĄtĂ©k lĂ©pĂ©störtĂ©netĂ©nek korĂĄbbi verziĂłit, Ă©s ezt kĂ©sƑbb ĂșjrahasznosĂ­thatjuk.

Våltozåsok észlelése

A vĂĄltozĂĄsok Ă©szlelĂ©se megvĂĄltoztathatĂł objektumokban nehĂ©z feladat, mivel azok közvetlenĂŒl mĂłdosĂ­thatĂłak. Ez az Ă©szlelĂ©s megköveteli a megvĂĄltoztathatĂł objektumtĂłl, hogy azt összehasonlĂ­tsuk sajĂĄt maga korĂĄbbi mĂĄsolataihoz Ă©s a teljes objektumfa bejĂĄrĂĄsĂĄt.

A vĂĄltozĂĄsok Ă©szlelĂ©se egy megvĂĄltoztathatatlan objektum esetĂ©n jelentƑsen egyszerƱbb. Ha a megvĂĄltoztathatatlan objektum, amire referĂĄlunk kĂŒlönbözik a korĂĄbbitĂłl, akkor az objektum megvĂĄltozott.

ÚjrarenderelĂ©s megĂĄllapĂ­tĂĄsa Reactben

A megvĂĄltoztathatatlansĂĄg legfƑbb elƑnye, hogy az segĂ­t a Reactben tiszta komponensek Ă©pĂ­tĂ©sĂ©ben. A megvĂĄltoztathatatlan adat könnyen megĂĄllapĂ­thatja, ha valamilyen vĂĄltozĂĄs törtĂ©nt, ami azt segĂ­t meghatĂĄrozni, hogy egy komponensnek Ășjra kell-e renderelnie.

A shouldComponentUpdate() metódusról, és hogy hogyan készíts tiszta komponenseket, a Teljesítmény optimalizålåsa olvasåsåval tanulhatsz többet.

FĂŒggvĂ©nykomponensek

Most ĂĄtalakĂ­tjuk a Square komponenst egy fĂŒggvĂ©nykomponenssĂ©.

A Reactben a fĂŒggvĂ©nykomponensek egy egyszerƱbb mĂłdja komponensek Ă­rĂĄsĂĄnak, amik csak egy render metĂłdust tartalmaznak, Ă©s nincs sajĂĄt ĂĄllapotuk. Ahelyett hogy osztĂĄlyokat definiĂĄlunk, amik a React.Component kiterjesztĂ©sei, Ă­rhatunk egy fĂŒggvĂ©nyt, ami veszi a prop-okat inputkĂ©nt, Ă©s azt adja vissza, amit renderelni kĂ©ne. A fĂŒggvĂ©nykomponensek Ă­rĂĄsa kevĂ©sbĂ© idƑigĂ©nyes, Ă©s sok komponens kifejezhetƑ Ă­gy.

CserĂ©ld le a Square osztĂĄlyt erre a fĂŒggvĂ©nyre:

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

LecserĂ©ltĂŒk a this.props-t props-ra mindkĂ©t helyen, ahol megjelent.

Nézd meg a teljes kódot ezen a ponton

Megjegyzés

Amikor mĂłdosĂ­tottuk a Square fĂŒggvĂ©nyt egy fĂŒggvĂ©nykomponensre, az onClick={() => this.props.onClick()}-t is megvĂĄltoztattuk egy rövidebb onClick={props.onClick} formĂĄra (vedd Ă©szre mindkĂ©t zĂĄrĂłjel hiĂĄnyĂĄt).

SzerepvĂĄltĂĄs

Most pedig ki kell javĂ­tanunk egy egyĂ©rtelmƱ hibĂĄt a tic-tac-toe jĂĄtĂ©kunkban: “O”-kat mĂ©g nem lehet felvinni a tĂĄblĂĄra.

Most beĂĄllĂ­tjuk az “X”-szet alapĂ©rtelmezett elsƑ lĂ©pĂ©snek. Az alapĂ©rtelmezett lĂ©pĂ©st a kezdƑ ĂĄllapot mĂłdosĂ­tĂĄsĂĄval ĂĄllĂ­thatjuk be, a Board konstruktorĂĄban:

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

Minden alkalommal, mikor egy jåtékos lép, az xIsNext (egy boolean) értékét våltogatjuk, hogy meg tudjuk hatårozni melyik jåtékos következik, és hogy a jåték ållåsa el legyen mentve. Módosítsuk a Board handleClick metódusåt, hogy våltogassa az xIsNext értékét:

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

Ezzel a vĂĄltoztatĂĄssal, az “X”-szek Ă©s az “O”-k most mĂĄr vĂĄltakoznak. PrĂłbĂĄld ki!

VĂĄltoztassuk meg a “stĂĄtusz” szöveget is a Board render metĂłdusĂĄban, hogy azt is mutassa, hogy ki a következƑ jĂĄtĂ©kos:

  render() {
    const status = 'KövetkezƑ jĂĄtĂ©kos: ' + (this.state.xIsNext ? 'X' : 'O');
    return (
      // mĂĄs vĂĄltozĂĄs nincs

Ezen våltoztatåsok utån a Board komponensed így kell hogy kinézzen:

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 = 'KövetkezƑ jĂĄtĂ©kos ' + (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>
    );
  }
}

Nézd meg a teljes kódot ezen a ponton

GyƑztes kihirdetĂ©se

Most hogy mĂĄr mutatjuk ki a következƑ jĂĄtĂ©kos, azt is mutatnunk kĂ©ne, ha a jĂĄtĂ©kot valaki megnyeri, Ă©s nincs több lĂ©pĂ©sre szĂŒksĂ©g. MĂĄsold be ezt a segĂ©dfĂŒggvĂ©nyt a fĂĄjl vĂ©gĂ©re:

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

Adott egy 9 nĂ©gyzetet tartalmazĂł tömb, ez a fĂŒggvĂ©ny leellenƑrzi, hogy van-e gyƑztes, Ă©s ennek megfelelƑen visszaadhat 'X'-szet, 'O'-t, vagy null Ă©rtĂ©ket.

A calculateWinner(squares) fĂŒggvĂ©nyt meghĂ­vjuk a Board render metĂłdusĂĄban, hogy megtudjuk van-e gyƑztes. Ha egy jĂĄtĂ©kos nyert, akkor mutathatunk valami olyan szöveget mint: “GyƑztes: X” vagy “GyƑztes: O”. CserĂ©ljĂŒk le a status deklarĂĄciĂłjĂĄt is a Board render metĂłdusĂĄban erre a kĂłdra:

  render() {
    const winner = calculateWinner(this.state.squares);    let status;    if (winner) {      status = 'GyƑztes: ' + winner;    } else {      status = 'KövetkezƑ jĂĄtĂ©kos ' + (this.state.xIsNext ? 'X' : 'O');    }
    return (
      // mĂĄs vĂĄltozĂĄs nincs

Most mĂĄr megvĂĄltoztathatjuk a Board handleClick metĂłdusĂĄt is, hogy az elƑbb tĂ©rjen vissza, ignorĂĄlva a kattintĂĄsokat, ha valaki megnyerte a jĂĄtĂ©kot, vagy ha a Square mĂĄr ki van töltve:

  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,
    });
  }

Nézd meg a teljes kódot ezen a ponton

GratulĂĄlunk! Most mĂĄr van egy mƱködƑ tic-tac-toe jĂĄtĂ©kod. És közben megtanultad a React alapjait is. SzĂłval igazĂĄbĂłl itt te vagy az igazi nyertes.

IdƑutazás hozzáadása

Egy utolsĂł gyakorlatkĂ©nt tegyĂŒk lehetƑvĂ© az “idƑutazĂĄst” korĂĄbbi jĂĄtĂ©kĂĄllĂĄsokhoz.

Lépéstörténet tårolåsa

Ha mutĂĄltuk (közvetlen megvĂĄltoztatĂĄs) volna a squares tömböt, az idƑutazĂĄs implementĂĄlĂĄsa nagyon bonyolult lenne.

Mi azonban a slice() metĂłdust hasznĂĄltuk Ășj squares tömb mĂĄsolatok lĂ©trehozĂĄsĂĄhoz minden lĂ©pĂ©snĂ©l, Ă©s Ă­gy megvĂĄltoztathatatlankĂ©nt kezeltĂŒk azt. Ez lehetƑvĂ© teszi majd a squares tömb minden korĂĄbbi verziĂłjĂĄnak eltĂĄrolĂĄsĂĄt, Ă©s hogy navigĂĄlni tudjunk a mĂĄr megtörtĂ©nt lĂ©pĂ©sek között.

A korĂĄbbi squares tömböket egy Ășj, history nevƱ tömbben fogjuk tĂĄrolni. A history tömb a tĂĄbla minden ĂĄllĂĄsĂĄt tartalmazza az elsƑtƑl az utolsĂł lĂ©pĂ©sig, Ă©s Ă­gy nĂ©z ki:

history = [
  // Az elsƑ lĂ©pĂ©s elƑtt
  {
    squares: [
      null, null, null,
      null, null, null,
      null, null, null,
    ]
  },
  // Az elsƑ lĂ©pĂ©s utĂĄn
  {
    squares: [
      null, null, null,
      null, 'X', null,
      null, null, null,
    ]
  },
  // A måsodik lépés utån
  {
    squares: [
      null, null, null,
      null, 'X', null,
      null, null, 'O',
    ]
  },
  // ...
]

Most el kell döntenĂŒnk, hogy melyik komponens birtokolja a history ĂĄllapotot.

Állapot ismételt felemelése

SzeretnĂ©nk, ha a legfelsƑbb szintƱ Game komponens mutatnĂĄ a korĂĄbbi lĂ©pĂ©sek listĂĄjĂĄt. Ehhez hozzĂĄ kell hogy fĂ©rjen a history-hoz, szĂłval a history ĂĄllapotot a legfelsƑbb szintƱ Game komponensbe helyezzĂŒk.

A history ĂĄllapot Game komponensben valĂł elhelyezĂ©sĂ©vel eltĂĄvolĂ­thatjuk a squares ĂĄllapotot annak gyermekkomponensĂ©bƑl, a Board komponensbƑl. UgyanĂșgy, ahogy a Square komponensbƑl “felemeltĂŒk az ĂĄllapotot” a Board komponensbe, most felemeljĂŒk azt a Board-bĂłl a legfelsƑbb szintƱ Game komponensbe. Ez teljes felĂŒgyeletet ad a Game komponensnek a Board adatai felett, Ă©s utasĂ­thatja a Board komponenst korĂĄbbi ĂĄllĂĄsok renderelĂ©sĂ©re a history-bĂłl.

ElƑször is beĂĄllĂ­tjuk a kezdeti ĂĄllapotot a Game komponens konstruktorĂĄban:

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>{/* stĂĄtusz */}</div>
          <ol>{/* TODO */}</ol>
        </div>
      </div>
    );
  }
}

EzutĂĄn beĂĄllĂ­tjuk, hogy a Board komponens a Game komponensbƑl fogadja a squares Ă©s onClick propokat. Mivel mĂĄr van egy egyszerƱ kattintĂĄskezelƑnk a Board-ban több Square komponenshez, meg kell mondanunk minden egyes Square komponens pozĂ­ciĂłjĂĄt is az onClick kezelƑnek, hogy jelezzĂŒk melyik Square-re kattintottak. Íme a szĂŒksĂ©ges lĂ©pĂ©sek a Board komponens ĂĄtalakĂ­tĂĄsĂĄhoz:

  • Töröld a constructor-t a Board-ban.
  • CserĂ©ld le a this.state.squares[i]-t this.props.squares[i]-re a Board renderSquare metĂłdusĂĄban.
  • CserĂ©ld le a this.handleClick(i)-t this.props.onClick(i)-re a Board renderSquare metĂłdusĂĄban.

A Board komponens most így néz ki:

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 = 'GyƑztes: ' + winner;
    } else {
      status = 'KövetkezƑ jĂĄtĂ©kos ' + (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>
    );
  }
}

FrissĂ­tsĂŒk a Game komponens render metĂłdusĂĄt, hogy az a legfrissebb history bejegyzĂ©st hasznĂĄlja a jĂĄtĂ©k stĂĄtuszĂĄnak megjelenĂ­tĂ©sĂ©hez:

  render() {
    const history = this.state.history;    const current = history[history.length - 1];    const winner = calculateWinner(current.squares);    let status;    if (winner) {      status = 'GyƑztes: ' + winner;    } else {      status = 'KövetkezƑ jĂĄtĂ©kos ' + (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>
    );
  }

Mivel most mĂĄr a Game komponens rendereli a jĂĄtĂ©k stĂĄtuszĂĄt, eltĂĄvolĂ­thatjuk az ennek megfelelƑ kĂłdot a Board render metĂłdusĂĄbĂłl. Az ĂșjraĂ­rĂĄs utĂĄn a Board render metĂłdusa Ă­gy nĂ©z ki:

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

UtoljĂĄra pedig ĂĄt kell helyeznĂŒnk a handleClick metĂłdust a Board komponensbƑl a Game komponensbe. MĂłdosĂ­tanunk is kell a handleClick-et, mivel a Game komponens ĂĄllapota mĂĄskĂ©nt van strukturĂĄlva. A Game handleClick metĂłdusĂĄban összefƱzzĂŒk a history bejegyzĂ©seket a history ĂĄllapotba.

  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,
    });
  }

Megjegyzés

A tömb push() metĂłdusĂĄval ellentĂ©tben, amit mĂĄr korĂĄbbrĂłl ismerhetsz, a concat() metĂłdus nem mĂłdosĂ­tja a korĂĄbbi tömböt, szĂłval ezt rĂ©szesĂ­tjĂŒk elƑnyben.

Ezen a ponton a Board komponensnek csak a renderSquare Ă©s render metĂłdusokra van szĂŒksĂ©ge. A jĂĄtĂ©k ĂĄllapota Ă©s a handleClick metĂłdusok a Game komponensben kell legyenek.

Nézd meg a teljes kódot ezen a ponton

Koråbbi lépések mutatåsa

Mivel feljegyezzĂŒk a tic-tac-toe jĂĄtĂ©k lĂ©pĂ©störtĂ©netĂ©t, most mĂĄr mutatni tudjuk a jĂĄtĂ©kosnak a korĂĄbbi lĂ©pĂ©sek listĂĄjĂĄt.

KorĂĄbban megtanultuk, hogy a React elemek elsƑosztĂĄlyĂș JavaScript objektumok; körbe tudjuk Ƒket kĂŒldözgetni az alkalmazĂĄsban. Hogy több elemet tudjunk renderelni Reactben, hasznĂĄlhatunk egy tömböt, ami React elemeket tartalmaz.

A JavaScriptben a tömbök rendelkeznek egy map() metódussal, amit gyakran hasznålnak adatok mås adatra leképezésére, mint példåul:

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

A map metĂłdus hasznĂĄlatĂĄval le tudjuk kĂ©pezni a lĂ©pĂ©störtĂ©netĂŒnket React elemekre, amik gombokat kĂ©pviselnek a kĂ©pernyƑn, Ă©s egy gomblistĂĄra, amikkel “ugrĂĄlni” tudunk korĂĄbbi lĂ©pĂ©sekre.

Most pedig kĂ©pezzĂŒk le a history-t a Game komponens render metĂłdusĂĄban:

  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 ?        'Menj ide, lépés: #' + move :        'Menj a jåték kezdetéhez';      return (        <li>          <button onClick={() => this.jumpTo(move)}>{desc}</button>        </li>      );    });
    let status;
    if (winner) {
      status = 'GyƑztes: ' + winner;
    } else {
      status = 'KövetkezƑ jĂĄtĂ©kos ' + (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>
    );
  }

Nézd meg a teljes kódot ezen a ponton

A tic-tac-toe jĂĄtĂ©k lĂ©pĂ©störtĂ©netĂ©ben minden lĂ©pĂ©shez lĂ©trehozunk egy <li>-t, ami tartalmaz egy <button> gombot. A gomb rendelkezik egy onClick kezelƑvel, ami meghĂ­v egy this.jumpTo() metĂłdust. A jumpTo() metĂłdust mĂ©g nem implementĂĄltuk. EgyenlƑre egy listĂĄt kell lĂĄtnunk a lĂ©pĂ©sekrƑl, amik mĂĄr megtörtĂ©ntek a jĂĄtĂ©k sorĂĄn, Ă©s egy figyelmeztetĂ©st a fejlesztƑi eszközözök konzolban, ami azt mondja:

Warning: Each child in an array or iterator should have a unique “key” prop. Check the render method of “Game”.

BeszĂ©ljĂŒk meg, mit is jelent a fenti figyelmeztetĂ©s.

AzonosĂ­tĂł kulcs vĂĄlasztĂĄsa

Amikor egy listĂĄt renderelĂŒnk, a React minden renderelt listaelemrƑl tĂĄrol valamennyi informĂĄciĂłt. Amikor frissĂ­tĂŒnk egy listĂĄt, a Reactnek tudnia kell, hogy mi vĂĄltozott. HozzĂĄ tudtunk adni, eltĂĄvolĂ­tani vagy megvĂĄltoztatni a lista sorrendjĂ©t, vagy megvĂĄltoztatni a lista elemeit.

KĂ©pzeld el az ĂĄtmenetet errƑl

<li>AladĂĄr: 7 feladat van vissza</li>
<li>BalĂĄzs: 5 feladat van vissza</li>

erre

<li>BalĂĄzs: 9 feladat van vissza</li>
<li>Klaudia: 8 feladat van vissza</li>
<li>AladĂĄr: 5 feladat van vissza</li>

Ha valaki ezt olvasnĂĄ a frissĂ­tett szĂĄmlĂĄlĂłk mellett, valĂłszĂ­nƱleg azt mondanĂĄ, hogy AladĂĄr Ă©s BalĂĄzs fel lett cserĂ©lve, Ă©s Klaudia be lett illesztve AladĂĄr Ă©s BalĂĄzs közĂ©. Azonban a React egy szĂĄmĂ­tĂłgĂ©pes program, nem Ă©rti mit is szerettĂŒnk volna elĂ©rni. Mivel a React nem ismeri a szĂĄndĂ©kainkat, minden listabejegyzĂ©snek meg kell adnunk egy kulcs tulajdonsĂĄgot, hogy az meg tudja kĂŒlönböztetni a bejegyzĂ©seket annak testvĂ©reitƑl. Egy alternatĂ­va lehet a sztringek aladar, balazs, klaudia hasznĂĄlata. Ha az adatokat egy adatbĂĄzisbĂłl jelenĂ­tenĂ©nk meg, akkor hasznĂĄlhatnĂĄnk AladĂĄr, BalĂĄzs Ă©s Klaudia adatbĂĄzis azonosĂ­tĂłkulcsĂĄt egyedi kulcsnak.

<li key={user.id}>{user.name}: {user.taskCount} feladat van vissza</li>

Mikor egy listĂĄt renderelĂŒnk Ășjra, a React veszi minden lista elemĂ©nek a kulcsĂĄt, Ă©s megkeresi az elƑzƑ listaelemet a megegyezƑ kulccsal. Ha a jelenlegi lista tartalmaz egy kulcsot, amit korĂĄbban nem, a React kĂ©szĂ­t egy Ășj komponenst. Ha a jelenlegi listĂĄban mĂĄr nincs jelen egy korĂĄbban lĂ©tezƑ kulcs, a React törli az elƑzƑ komponenst. Ha kĂ©t kulcs egyezik, a komponens mozgatva lett. A kulcsok azonosĂ­tanak minden komponenst a Reactnek, ami lehetƑvĂ© teszi annak az ĂĄllapotok kezelĂ©sĂ©t ĂșjrarenderelĂ©sek között. Ha egy komponens kulcsa megvĂĄltozik, a komponens törölve lesz Ă©s Ășjra lĂ©tre lesz hozva, egy Ășj ĂĄllapottal.

A key egy speciĂĄlis fenntartott tulajdonsĂĄg a Reactben (a ref-el egyetemben, ami egy sokkal haladĂłbb funkciĂł). Mikor egy Ășj elemet kĂ©szĂ­tĂŒnk, a React kivonja a key tulajdonsĂĄgot, Ă©s közvetlenĂŒl eltĂĄrolja azt a visszaadott elemen. Ha Ășgy is tƱnik, hogy a key a prop-ok közĂ© tartozik, a key-re nem tudunk referĂĄlni a this.prop.key-vel. A React automatikusan hasznĂĄlja a key-t, hogy eldöntse melyik komponenseket frissĂ­tse. Egy komponens nem tud annak key tulajdonsĂĄgĂĄrĂłl Ă©rdeklƑdni.

ErƑsen ajĂĄnlott, hogy dinamikus listĂĄk esetĂ©ben rendes kulcsokat rendelj hozzĂĄ a listaelemekhez. Ha nem rendelkezel egy megfelelƑ kulccsal, fontold meg az adatod ĂĄtalakĂ­tĂĄsĂĄt, hogy lĂ©tezzen megfelelƑ kulcs.

Ha nincs kulcs megadva, a React egy figyelmeztetĂ©st fog adni, Ă©s a tömb indexĂ©t fogja megadni alapĂ©rtelmezett kulcskĂ©nt. A tömb indexĂ©nek kulcskĂ©nt valĂł hasznĂĄlata problĂ©mĂĄs lehet, ha egy lista Ășjra lesz rendezve, vagy Ășj elem lesz hozzĂĄadva, vagy egy elem lesz törölve. A key={i} hatĂĄrozott megadĂĄsa elnĂ©mĂ­tja a figyelmeztetĂ©st, de ugyanaz a problĂ©ma ĂĄll majd fenn, mint a tömbindexek esetĂ©ben, Ă©s a legtöbb esetben nem ajĂĄnlott.

A kulcsoknak nem kell globĂĄlisan egyedinek lenniĂŒk; csupĂĄn komponensek, Ă©s azok testvĂ©rei között kell, hogy egyediek legyenek.

IdƑutazás implementálása

A tic-tac-toe jåték lépéstörténetében minden koråbbi lépésnek van egy egyedi azonosítója: ez a lépés sorszåma. A lépések soha nincsenek åtrendezve, törölve, beillesztve mås lépések közé, szóval ebben az esetben tömbindex hasznålata kulcsként biztonsågosnak szåmít.

A Game komponens render metódusåban hozzå tudjuk adni a kulcsot így <li key={move}> és a React figyelmeztetése a kulcsokról el kell, hogy tƱnjön:

    const moves = history.map((step, move) => {
      const desc = move ?
        'Menj erre ide, lépés: #' + move :
        'Menj a jåték kezdetéhez';
      return (
        <li key={move}>          <button onClick={() => this.jumpTo(move)}>{desc}</button>
        </li>
      );
    });

Nézd meg a teljes kódot ezen a ponton

BĂĄrmelyik listabejegyzĂ©s gombjĂĄra kattintva egy hiba ugrik fel, mert a jumpTo metĂłdus nincs definiĂĄlva. MielƑtt implementĂĄlnĂĄnk a jumpTo metĂłdust, adjuk hozzĂĄ a stepNumber-t a Game komponens ĂĄllapotĂĄhoz, hogy jelezzĂŒk melyik lĂ©pĂ©st lĂĄtjuk Ă©ppen.

ElƑször is add hozzĂĄ a stepNumber: 0-t a kezdeti ĂĄllapothoz a Game contructor-ban:

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

Ezutån definiåljuk a jumpTo metódust a Game komponensben, hogy frissíteni tudjuk a stepNumber-t. Valamint az xIsNext értékét is igazra ållítjuk, ha egy påros szåmot våltoztatunk:

  handleClick(i) {
    // ez a metĂłdus nem vĂĄltozott
  }

  jumpTo(step) {    this.setState({      stepNumber: step,      xIsNext: (step % 2) === 0,    });  }
  render() {
    // ez a metĂłdus nem vĂĄltozott
  }

Most pedig egy pĂĄr vĂĄltoztatĂĄst eszközölĂŒnk a Game handleClick metĂłdusĂĄban, egy nĂ©gyzetre kattintĂĄskor lesz meghĂ­vva.

A hozzĂĄadott stepNumber ĂĄllapot most mĂĄr tĂŒkrözi a felhasznĂĄlĂł ĂĄltal lĂĄtott lĂ©pĂ©st. Ha lĂ©pĂŒnk egyet, frissĂ­tenĂŒnk kell a stepNumber-t azzal, hogy hozzĂĄadjuk azt a this.setState argumentumĂĄhoz: stepNumber: history.length. Ez azt biztosĂ­tja, hogy ne ragadjunk le mindig ugyanannak a lĂ©pĂ©snek a mutatĂĄsĂĄval minden Ășj lĂ©pĂ©s utĂĄn.

CserĂ©ljĂŒk le a this.state.history olvasĂĄsĂĄt is erre: this.state.history.slice(0, this.state.stepNumber + 1). Ez azt biztosĂ­tja, hogy ha “visszautazunk az idƑben”, Ă©s egy Ășjat lĂ©pĂŒnk, akkor eltekintĂŒnk a “jövƑ” lĂ©pĂ©seitƑl, amik Ă­gy mĂĄr helytelenek lennĂ©nek.

  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,
    });
  }

VĂ©gezetĂŒl mĂłdosĂ­tsuk a Game komponens render metĂłdusĂĄt, hogy a mindig legutolsĂł lĂ©pĂ©s renderelĂ©se helyett, inkĂĄbb a kivĂĄlasztott lĂ©pĂ©st renderelje, amit stepNumber hatĂĄroz meg:

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

    // mĂĄs vĂĄltozĂĄs nincs

Ha most rĂĄkattintunk bĂĄrmelyik lĂ©pĂ©sre a jĂĄtĂ©ktörtĂ©netben, a tic-tac-toa tĂĄbla azonnal Ășgy frissĂŒl, hogy azt az ĂĄllĂĄst mutassa, ami a rĂĄkattintott lĂ©pĂ©s utĂĄn törtĂ©nt.

Nézd meg a teljes kódot ezen a ponton

ÖsszegzĂ©s

Gratulålunk! Készítettél egy tic-tac-toe jåtékot ami:

  • LehetƑvĂ© teszi a tic-tac-toe jĂĄtszĂĄsĂĄt,
  • Kihirdeti a gyƑztest,
  • Elmenti a jĂĄtĂ©k lĂ©pĂ©störtĂ©netĂ©t a jĂĄtĂ©k közben,
  • LehetƑvĂ© teszi a jĂĄtĂ©kosoknak ĂșjranĂ©zni a jĂĄtĂ©ktĂĄbla korĂĄbbi ĂĄllĂĄsait.

SzĂ©p munka! RemĂ©ljĂŒk, hogy ezek utĂĄn te is Ășgy Ă©rzed, sikerĂŒlt megragadnod a React mƱködĂ©sĂ©nek lĂ©nyegĂ©t.

Nézd meg a végeredményt itt: Végeredmény.

Ha van egy kis extra idƑd, vagy szeretnĂ©d gyakorolni a React Ășj kĂ©pessĂ©geidet, Ă­me pĂĄr ötlet a tic-tac-toe jĂĄtĂ©k tökĂ©letesĂ­tĂ©sĂ©hez, nehĂ©zsĂ©g szerint növekvƑ sorrendben:

  1. Mutasd minden lépés pozíciójåt az (oszlop, sor) formåtumban a lépéstörténet liståban.
  2. Tedd félkövérré az aktuålisan kivålasztott elemet a lépés liståban.
  3. Írd ĂĄt a Board komponenst Ășgy, hogy az kĂ©t ciklust hasznĂĄljon nĂ©gyzetek kĂ©szĂ­tĂ©sĂ©hez belekĂłdolĂĄs helyett.
  4. Adj hozzĂĄ egy kapcsolĂł gombot, ami lehetƑvĂ© teszi a lĂ©pĂ©sek szortĂ­rozĂĄsĂĄt növekvƑ vagy csökkenƑ sorrendben.
  5. Ha valaki nyer, emeld ki a hĂĄrom nĂ©gyzetet, ami lehetƑvĂ© tette a jĂĄtĂ©kosnak a nyerĂ©st.
  6. Ha senki sem nyer, mutass egy ĂŒzenetet döntetlen eredmĂ©nyrƑl.

Ezen a tutoriĂĄlon keresztĂŒl olyan React koncepciĂłkat Ă©rintettĂŒnk mint elemek, komponensek, propok Ă©s ĂĄllapot. Ezen tĂ©mĂĄk rĂ©szletesebb magyarĂĄzatĂĄhoz nĂ©zd meg a dokumentĂĄciĂł többi rĂ©szĂ©t. Ha többet szeretnĂ©l tanulni komponensek definiĂĄlĂĄsrĂłl, nĂ©zd meg a React.Component API hivatkozĂĄsĂĄt.

Hasznos volt ez az oldal?Az oldal szerkesztése