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:
- BeĂĄllĂtĂĄsok a TutoriĂĄlhoz ad egy kezdĆpontot a tutoriĂĄl követĂ©sĂ©hez.
- ĂttekintĂ©s megtanĂtja a React alapokat: komponensek, propok, Ă©s a state (ĂĄllapot).
- JĂĄtĂ©k befejezĂ©se segĂt a leggyakoribb technikĂĄk elsajĂĄtĂtĂĄsĂĄban a Reactben.
- IdĆutazĂĄs hozzĂĄadĂĄsa egy ĂĄtfogĂłbb kĂ©pet ad a React egyedi erĆssĂ©geirĆl.
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:
- GyĆzĆdj meg rĂłla, hogy a Node.js egy jelenlegi verziĂłja telepĂtve van.
- 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
- 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 ..
- Hozz létre egy
index.css
fĂĄjlt asrc/
mappåban, tartalma pedig legyen ez a CSS kód. - Hozz létre egy
index.js
fĂĄjlt asrc/
mappĂĄban, tartalma pedig legyen ez a JS kĂłd. - Add hozzĂĄ a következĆ hĂĄrom sort az
index.js
fåjl tetejéhez, asrc/
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 ... */)
);
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:
Utåna: Egy szåmot kell låss minden négyzetben a renderelés sorån.
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Ă©ntonClick
nĂ©ven. A React csak kattintĂĄs utĂĄn fogja meghĂvni ezt a fĂŒggvĂ©nyt. Gyakori hiba csak ennyit ĂrnionClick={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 egyconstructor
-ral, egysuper(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
-tthis.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
ésonClick
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.

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:
- Jelentkezz be, vagy regisztrĂĄlj, Ă©s erĆsĂtsd meg az e-mailed (spam elkerĂŒlĂ©se Ă©rdekĂ©ben).
- Kattints a âForkâ gombra.
- Kattints a âChange Viewâ-ra, majd vĂĄlaszd a âDebug modeâ-t.
- 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
-tthis.props.value
-ra a Squarerender
metódusåban - Cseréld le a
this.setState()
-tthis.props.onClick()
-re a Squarerender
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:
- Az
onClick
prop a beĂ©pĂtett DOM<button>
komponensben közli a Reacttel, hogy ĂĄllĂtson fel egy esemĂ©nyfigyelĆt kattintĂĄsra. - Amikor a gombra kattintanak, a React meghĂvja az
onClick
esemĂ©nyfigyelĆt, ami a Square komponensrender()
metĂłdusĂĄban definiĂĄlva lett. - Ez az esemĂ©nyfigyelĆ meghĂvja a
this.props.onClick()
fĂŒggvĂ©nyt. A SquareonClick
propja a Board komponensben lett definiĂĄlva. - Mivel a Board lekĂŒldte az
onClick={() => this.handleClick(i)}
propot a Square komponensnek, a Square meghĂvja athis.handleClick(i)
fĂŒggvĂ©nyt, ha rĂĄkattintanak. - 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>
gombelemonClick
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 SquareonClick
propjĂĄnak mĂĄs nevet is adhatunk, vagy akĂĄr a BoardhandleClick
metĂłdusĂĄnak, Ă©s a kĂłd ugyanĂșgy mƱködne. A Reactben, közös megĂĄllapodĂĄs alapjĂĄnon[Event]
-nek hĂvjuk azokat a propokat, amik esemĂ©nyeket kĂ©pviselnek Ă©shandle[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övidebbonClick={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]
-tthis.props.squares[i]
-re a BoardrenderSquare
metódusåban. - Cseréld le a
this.handleClick(i)
-tthis.props.onClick(i)
-re a BoardrenderSquare
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, aconcat()
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:
- Mutasd minden lĂ©pĂ©s pozĂciĂłjĂĄt az (oszlop, sor) formĂĄtumban a lĂ©pĂ©störtĂ©net listĂĄban.
- Tedd félkövérré az aktuålisan kivålasztott elemet a lépés liståban.
- Ărd ĂĄt a Board komponenst Ășgy, hogy az kĂ©t ciklust hasznĂĄljon nĂ©gyzetek kĂ©szĂtĂ©sĂ©hez belekĂłdolĂĄs helyett.
- Adj hozzĂĄ egy kapcsolĂł gombot, ami lehetĆvĂ© teszi a lĂ©pĂ©sek szortĂrozĂĄsĂĄt növekvĆ vagy csökkenĆ sorrendben.
- Ha valaki nyer, emeld ki a hĂĄrom nĂ©gyzetet, ami lehetĆvĂ© tette a jĂĄtĂ©kosnak a nyerĂ©st.
- 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.