Öğretici: Tic-Tac-Toe
Bu öğretici ile küçük bir tic-tac-toe oyunu yapacaksınız. Bu öğretici herhangi bir React bilgisi gerektirmez. Bu öğreticide öğreneceğiniz teknikler herhangi bir React uygulaması yapmak için gerekli olan temel tekniklerdir ve bunları tamamıyla anlamak React hakkında derinlemesine bir anlayış sağlayacaktır.
Bu öğretici birçok bölüme ayrılmıştır:
- Öğretici için kurulum bölümü size öğreticiyi takip etmeniz için bir başlangıç noktası verecektir.
- Genel bakış bölümü React’in temellerini öğretecektir: bileşenler, prop’lar ve state.
- Oyunu tamamlama bölümü React ile geliştirme yaparken sık kullanılan teknikleri öğretecektir.
- Zaman yolculuğu ekleme bölümü React’in eşsiz gücüne daha derin bir bakış getirecektir.
Ne yapıyorsunuz?
Bu öğreticide, React ile interektif bir tic-tac-toe oyunu yapacaksınız.
İşimiz bittiği zaman nasıl bir şeyin ortaya çıkacağını burada görebilirsiniz:
import { useState } from 'react'; function Square({ value, onSquareClick }) { return ( <button className="square" onClick={onSquareClick}> {value} </button> ); } function Board({ xIsNext, squares, onPlay }) { function handleClick(i) { if (calculateWinner(squares) || squares[i]) { return; } const nextSquares = squares.slice(); if (xIsNext) { nextSquares[i] = 'X'; } else { nextSquares[i] = 'O'; } onPlay(nextSquares); } const winner = calculateWinner(squares); let status; if (winner) { status = 'Kazanan: ' + winner; } else { status = 'Sıradaki oyuncu: ' + (xIsNext ? 'X' : 'O'); } return ( <> <div className="status">{status}</div> <div className="board-row"> <Square value={squares[0]} onSquareClick={() => handleClick(0)} /> <Square value={squares[1]} onSquareClick={() => handleClick(1)} /> <Square value={squares[2]} onSquareClick={() => handleClick(2)} /> </div> <div className="board-row"> <Square value={squares[3]} onSquareClick={() => handleClick(3)} /> <Square value={squares[4]} onSquareClick={() => handleClick(4)} /> <Square value={squares[5]} onSquareClick={() => handleClick(5)} /> </div> <div className="board-row"> <Square value={squares[6]} onSquareClick={() => handleClick(6)} /> <Square value={squares[7]} onSquareClick={() => handleClick(7)} /> <Square value={squares[8]} onSquareClick={() => handleClick(8)} /> </div> </> ); } export default function Game() { const [history, setHistory] = useState([Array(9).fill(null)]); const [currentMove, setCurrentMove] = useState(0); const xIsNext = currentMove % 2 === 0; const currentSquares = history[currentMove]; function handlePlay(nextSquares) { const nextHistory = [...history.slice(0, currentMove + 1), nextSquares]; setHistory(nextHistory); setCurrentMove(nextHistory.length - 1); } function jumpTo(nextMove) { setCurrentMove(nextMove); } const moves = history.map((squares, move) => { let description; if (move > 0) { description = '# numaralı harekete git' + move; } else { description = 'Oyunun başlangıcına git'; } return ( <li key={move}> <button onClick={() => jumpTo(move)}>{description}</button> </li> ); }); return ( <div className="game"> <div className="game-board"> <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} /> </div> <div className="game-info"> <ol>{moves}</ol> </div> </div> ); } 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; }
Eğer bu kod size şu an anlamlı gelmiyorsa ya da kodun sözdizimine yabancıysanız, merak etmeyin! Bu öğreticinin amacı React’i ve sözdizimini anlamanıza yardımcı olmaktır.
Öğreticiye devam etmeden önce yukarıdaki tic-tac-toe oyununu kontrol etmenizi tavsiye ederiz. Fark edeceğiniz özelliklerden biri de oyun tahtasının sağında numaralandırılmış bir listenin bulunmasıdır. Bu liste size oyun boyunca yapılmış tüm hamleleri gösterir ve oyun devam ettiği sürece bu liste güncellenir.
Tamamlanmış oyunla biraz vakit geçirdikten sonra devam edin. Bu öğreticide daha basit bir şablonla başlayacaksınız. Bir sonraki adımımız oyunu yapmaya başlamanız için sizi hazırlamaktır.
Öğretici için kurulum
Aşağıdaki canlı kod editöründe, sağ üst köşedeki Fork butonuna basın ve CodeSanbox websitesini kullanarak editörü yeni bir sayfada açın. CodeSandbox, tarayıcınızda kod yazmanıza ve kullanıcılarınızın yaptığınız uygulamayı nasıl göreceğini önizlemenize olanak sağlar. Açılan yeni sayfa boş bir kare ve bu öğretici için gerekli olan başlangıç kodunu gösterecektir.
export default function Square() { return <button className="square">X</button>; }
Genel bakış
Kurulumu tamamladığımıza göre, React’e genel bir bakış atalım!
Başlangıç kodunu inceleme
CodeSanbox’ta üç ana bölüm göreceksiniz:
srcklasöründeApp.js,index.js,styles.cssgibi dosyaların listelendiği Files bölümü vepublicadında bir klasör- Seçtiğin dosyanın kaynak kodunu göreceğin code editor
- Yazdığın kodun nasıl görüntüleneceğini göreceğin browser bölümü
Files bölümünde App.js dosyası seçili olmalıdır. O dosyanın içeriği kod editöründe şöyle olmalıdır:
export default function Square() {
return <button className="square">X</button>;
}Tarayıcı bölümü, içinde X olan bir kare göstermelidir:
Şimdi başlangıç kodundaki dosyalara bir göz atalım.
App.js
App.js içindeki kod bir bileşen yaratır. React’te bileşen, bir kullanıcı arayüzü parçasını temsil eden tekrar kullanılabilir bir kod parçasıdır. Bileşenler, uygulamanızdaki UI elementleri render etmek, yönetmek ve güncellemek için kullanılır. Hadi şu bileşene satır satır bakarak ne olduğunu anlamaya çalışalım:
export default function Square() {
return <button className="square">X</button>;
}İlk satır Square adında bir fonksiyon tanımlar. export JavaScript kelimesi bu fonksiyonun bu dosya dışındaki yerlerde de kullanılabilmesi sağlar. default kelimesi ise bu fonksiyonu kullanan diğer dosyalara bu fonksiyonun dosyadaki ana fonksiyon olduğunu söyler.
export default function Square() {
return <button className="square">X</button>;
}İkinci satır bir buton döndürür. return JavaScript kelimesi bundan sonra gelen her şeyin, fonksiyonu çağırana bir değer olarak döndürüleceği anlamına gelir. <button> bir JSX elementidir. JSX elemanı, neyi görüntülemek istediğinizi açıklayan JavaScript kodu ve HTML elemanlarının birleşimidir. className="square" ise bir buton özelliği ya da CSS’e butonu nasıl stillendireceğini söyleyen prop’tur. X buton içindeki metindir ve </button> ifadesi JSX elementini kapatarak bundan sonra gelen içeriğin buton içine konmaması gerektiğini belirtir.
styles.css
CodeSandbox’ın Files bölümündeki styles.css isimli dosyaya tıklayın. Bu dosya React uygulamanızın stillerini tanımlar. İlk iki CSS seçici (* ve body) uygulamanızın büyük bölümlerinini stilini tanımlarken, .square seçicisi className özelliğini .square olduğu her bileşenin stilini tanımlar. Kodunuzda bu, App.js dosyasındaki Square bileşeninizdeki düğmeyle eşleşir.
index.js
CodeSandbox’ın Files bölümündeki index.js isimli dosyaya tıklayın. Öğretici sırasında bu dosyayı düzenlemeyeceksiniz ama bu dosya App.js dosyası içerisinde oluşturduğunuz bileşen ile web tarayıcısı arasındaki köprüdür.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './styles.css';
import App from './App';1-5. satırlar gerekli tüm parçaları bir araya getirir:
- React
- React’in web tarayıcıları ile konuşması için kütüphane (React DOM)
- bileşenlerinizin stilleri
App.jsiçerisinde oluşturduğunuz bileşen.
Sayfanın geri kalanı tüm parçaları bir araya getirir ve public dosyasındaki index.html dosyasına son ürünü enjekte eder.
Tahtayı oluşturmak
App.js dosyasına geri gidelim. Öğreticinin geri kalanını bu sayfada geçireceğiz.
Şu anda tahta tek bir kareden oluşmakta ama bizim dokuz taneye ihtiyacımız var! Şu şekilde kopyala yapıştır yaparak iki kare yapmak isterseniz:
export default function Square() {
return <button className="square">X</button><button className="square">X</button>;
}Şu hatayı alacaksınız:
<>...</> mi kullanmak istediniz?React bileşenleri tek bir JSX elementi döndürmelidir ve birden fazla bitişik iki buton gibi JSX elementi döndürmemelidir. Bunu düzeltmek için Fragment (<> ve </>) kullanıp çoklu bitişik JSX elementlerini şu şekilde sarabilirsiniz:
export default function Square() {
return (
<>
<button className="square">X</button>
<button className="square">X</button>
</>
);
}Şimdi şunu görmelisiniz:
Güzel! Şimdi yapmanız gereken bir kaç kere daha kopyala yapıştır yaparak dokuz kare elde etmek ve…
Olamaz! Kareler bizim istediğimiz tahtadaki gibi grid haline değil, tek bir satırda. Bunu düzeltmek için karelerinizi divler kullanarak satırlar halinde gruplamanız ve bazı CSS sınıfları eklemeniz gerekmektedir. Bunu yaparken aynı zamanda her karenin nerede görüntülendiğini bilmek için her kareye bir numara vereceksiniz.
App.js dosyası içinide, Square bileşenini şöyle gözükecek halde güncelleyin:
export default function Square() {
return (
<>
<div className="board-row">
<button className="square">1</button>
<button className="square">2</button>
<button className="square">3</button>
</div>
<div className="board-row">
<button className="square">4</button>
<button className="square">5</button>
<button className="square">6</button>
</div>
<div className="board-row">
<button className="square">7</button>
<button className="square">8</button>
<button className="square">9</button>
</div>
</>
);
}styles.css dosyası içinde tanımlanmış CSS, className’i board-row olan div’leri stiller. Bileşenleri stillenmiş div’ler ile satır halinde grupladığınza göre, tic-tac-toe tahtamızı elde etmiş oluruz:
Ancak şimdi bir sorunumuz var. Square adlı bileşeniniz, artık bir kare değil. Bu sorunu bileşenin ismini Board olarak değiştirerek çözelim:
export default function Board() {
//...
}Bu noktada kodunuz şöyle gözükmelidir:
export default function Board() { return ( <> <div className="board-row"> <button className="square">1</button> <button className="square">2</button> <button className="square">3</button> </div> <div className="board-row"> <button className="square">4</button> <button className="square">5</button> <button className="square">6</button> </div> <div className="board-row"> <button className="square">7</button> <button className="square">8</button>