github.com/omergulcicek/reactjs
Tüm Türkçe dokümanlar için: turkcedokuman.com
Bu pdf 21 Eylül 2018 tarihinde hazırlanmıştır.
Online olarak katılmayı deneyin ya da local geliştirme ortamınızı oluşturun.
React'i online kod ortamında denemek istiyorsanız, CodePen ya da CodeSandbox sitelerini kullanabilirsiniz.
Kendi text editörünüzü kullanmayı tercih ederseniz, bu HTML dosyasını indirebilir, düzenleyebilir ve tarayıcınızdaki local dosya sisteminden açabilirsiniz. Kod dönüştürmesini yavaş yapar, bu nedenle sadece öğrenme aşamasında kullanın, projeleriniz için kullanmayın.
React kavramlarına adım adım bir giriş için hızlı başlangıç bölümüne gidin.
Bir örnek üzerinden eğitim için uygulamalı eğitim bölümünü deneyin.
Yukarıdaki hafif çözümler, React'a yeni başladıysanız ya da denemek için en uygun yöntemlerdir.
React JS'i bilgisayarınıza kurup, localinizde proje geliştirmeye başlamak istiyorsanız aşağıdaki adımları inceleyin.
Not:
Olası uyumsuzluk sorunlarını önlemek için tüm React paketleri aynı sürümü kullanmalıdır. (
react
,react-dom
vs.)
NodeJS sitesine girip aşağıda görüldüğü gibi soldaki butona tıklayıp nodejs'i indiriyoruz.
Ardından klasik kurulumu tamamladıktan sonra Node.js command prompt programını çalıştırıp, kurulumlarımızı bu konsol üzerinden yapacağız.
Yeni bir React projesine başlamak için en kolay yol, bir başlangıç kiti kullanmaktır.
React ekibi tarafından önerilen çeşitli başlangıç kitleri bulunmakta; Create React App, Next.js, Gatsby, nwb, razzle, Neutrino. React projesi başlatmak için resmi olarak desteklenen Create React App'ı detaylı açıklayalım.
Create React App, yeni bir React single page application'a başlamanın en iyi yoludur. Geliştirme ortamınızı, en yeni JavaScript özelliklerini kullanabilmenizi, güzel bir geliştirici deneyimi yaşamanızı ve uygulamanızı üretim için optimize etmenizi sağlar. NodeJS 6 veya daha üst sürümünün bilgisayarınızda kurulu olması gerekir (Sunucuda gerekli değildir).
Create React App'i özetlemek gerekirse bu, React uygulamaları oluşturmanız için ihtiyacınız olan birçok şeyi içerisinde barındıran bir pakettir.
İçerisine neler dahil edilmiştir?
- React, JSX, ES6, Flow syntax desteği
- Autoprefixed CSS ile otomatik olarak düzeltilen CSS'ler (-webkit veya diğer ön eklere gerek yoktur)
- Genel hatalar konusunda uyaran bir canlı geliştirme sunucusu
- Kod üzerinde yaptığımız değişiklikleri kaydettiğimiz anda arayüze yansıması için hot reloading
- JavaScript kod standartlarına uygun yazmanız için ESLint vs
İlk olarak nodejs kurup, ardından aşağıdaki adımları gerçekleştirerek ilk uygulamamızı oluşturmaya başlayalım.
npm install -g create-react-app
Ardından my-app
adında bir proje oluşturuyoruz.
create-react-app my-app
Kurulum başarılı olduğunda aşağıdaki gibi bir çıktı ile kaşılacaksınız.
Eğer npm 5.2.0+ sürümü yüklüyse, bunun yerine npx kullanabilirsiniz.
npx create-react-app my-app
Bu komutlar geçerli klasörde my-app
isimli bir dizin oluşturacaktır.
Bu dizinin içinde, proje yapısını oluşturacak ve geçişli bağımlılıkları kuracaktır.
Projenin klasör yapısı aşağıdaki gibi olacaktır. Yapılandırma veya karmaşık klasör yapıları yok, sadece uygulamanızı oluştururken gereken dosyaları içerir.
my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ └── favicon.ico
│ └── index.html
│ └── manifest.json
└── src
└── App.css
└── App.js
└── App.test.js
└── index.css
└── index.js
└── logo.svg
└── registerServiceWorker.js
Kurulum tamamlandıktan sonra aşağıdaki komutlar ile proje klasörünüze girebilirsiniz. Ardından npm start
ya da yarn start
komutu ile projenizi localde açabilirsiniz.
cd my-app
npm start
Otomatik olarak localhost sayfası açılacaktır. Kodda değişiklik yaparsanız, sayfa otomatik olarak yeniden yüklenir. Konsolda ise hata uyarılarını görürsünüz. Kod yazmaya başlamak için src/App.js
dosyasını düzenleyin ve kaydedin.
Kodunuzu yazdınız ve sunucunuza yüklemek istiyorsunuz, o halde npm run build
komutu ile üretim için gerekli klasörler hazırlanır. React'ı düzgün bir şekilde paketler ve yapıyı en iyi performans için optimize eder. CSS ve JavaScript kodlarını minified eder (sıkıştırır) ve rastgele isimlendirir.
npm run build
Oluşturulan build
klasörünün içerisindeki dosyaları sunucunuza atarak test edebilirsiniz.
React'i kullanmanın en kolay yolu, CodePen'deki bu Merhaba Dünya örnek kodunu kullanmaktır.
Herhangi bir şey yüklemenize gerek yoktur (React JS, React DOM ve Babel eklenmiştir). Dokümantasyonu farklı bir sekmede açıp codepen üzerinden kodları test edebilirsiniz.
Yerel bir geliştirme ortamı kullanmayı tercih ediyorsanız kurulum sayfasını inceleyin.
En küçük React örneği şuna benzer:
ReactDOM.render(
<h1>Merhaba Dünya!</h1>,
document.getElementById('root')
);
Bu kod, HTML'de root id'li etiket içerisine h1 başlığı ile Merhaba Dünya yazacaktır.
Bundan sonraki bölümlerde, React aşamalı olarak anlatılacaktır. React uygulamalarının yapı taşları olan element ve componentleri inceleyeceğiz.
Component konusuna hakim olduktan sonra, küçük ve yeniden kullanılabilir parçalardan karmaşık uygulamalar oluşturabilirsiniz.
React bir JavaScript kütüphanesidir. Bu nedenle JavaScript dilini temel düzeyde bilmeniz gerekiyor.
Kendinizi yeterli görmüyorsanız JavaScript bilgilerinizi yenilemenizi öneririz, böylece konuları daha kolay anlayabilirsiniz.
Ek kaynaklar; w3schools, codecademy, youtube
Böyle bir değişken tanımladığımızı düşünün:
const element = <h1>Merhaba Dünya!</h1>;
Bu string ya da HTML değildir.
Buna JSX denir, JavaScript için bir syntax uzantısıdır.
JSX, React elementleri üretir. Bir sonraki bölümde bunları DOM'a render etmeyi keşfedeceğiz.
DOM'a render etmek, yazdığınız React kodunun derlenip HTML'e yerleştirilme işlemine denir. Bir sonraki sayfada detaylıca anlatılacaktır.
Başlamanız için gerekli olan JSX'in temel bilgilerine aşağıdan erişebilirsiniz.
React, olayların nasıl işlendiğini, durumun zaman içinde nasıl değiştiğini kontrol eder ve bunları arayüze aktarır.
React yazmak için JSX'i kullanmak zorunda değilsiniz. Fakat yazım şekli HTML'i andırdığı için kolayca alışacak ve hızlıca kod yazabileceksiniz. Ayrıca React'in daha kullanışlı hata ve uyarı mesajları göstermesine izin verir.
O halde JSX yazmaya başlayalım!
Herhangi bir JavaScript ifadesini JSX'de süslü parantez içine sarmalayarak yerleştirebilirsiniz.
Örneğin; { 2 + 2 }
, { user.firstName }
ve { formatName(user) }
gibi sayısal işlem, obje, değişken, fonksiyon vb kullanabilirsiniz.
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Ömer',
lastName: 'Gülçiçek'
};
const element = (
<h1>
Merhaba, {formatName(user)}!
</h1>
);
ReactDOM.render(
element,
document.getElementById('root')
);
Derleme sonrasında, JSX ifadeleri JavaScript fonksiyonları haline gelir.
Yani, if deyimleri ve döngüler içinde JSX'i kullanabilir ayrıca bir fonksiyondan JSX return edebiliriz.
function getGreeting(user) {
if (user) {
return <h1>Merhaba {formatName(user)}!</h1>;
}
return <h1>Merhaba yabancı.</h1>;
}
const element = <div tabIndex="0"></div>;
Bir attribute'e JavaScript ifadesi yerleştirmek için süslü parantezleri de kullanabilirsiniz:
const element = <img src={user.avatarUrl}></img>;
Bir attribute'e JavaScript ifadesi yerleştirirken süslü parantezler arasına tırnak işareti koymayın. Tırnak işaretleri string değerler için ve süslü parantezler ise JavaScript ifadeleri için kullanmanız gerekir. Her ikisi birden aynı attribute'te kullanılamaz.
Uyarı:
JSX JavaScript'e HTML'den daha yakın olduğundan ReactDOM, HTML nitelik adları yerine
camelCase
adlandırma kuralını kullanır.Örneğin JSX'te
class
yerineclassName
,tabindex
yerinetabIndex
kullanılır.
Bir etiket boşsa (child içermiyorsa manasında), XML gibi hemen />
ile kapatabilirsiniz:
const element = <img src={user.avatarUrl} />;
JSX etiketleri child içerebilir:
const element = (
<div>
<h1>Merhaba!</h1>
<h2>Seni burada görmek güzel.</h2>
</div>
);
JSX elementindeki ana kapsayıcının içindeki ifadeler child
(çocuk) olarak adlandırılır. Örneğin, üstteki ifadede <div>
ana kapsayıcısının içerisinde bulunan <h1>
ve <h2>
etiketleri child olarak adlandırılır.
Input'tan gelen içeriği JSX'e yerleştirmek güvenlidir:
const title = response.inputtanGelenKotuNiyetliGiris;
// bu güvenlidir:
const element = <h1>{title}</h1>;
Varsayılan olarak React DOM, JSX'in içindeki herhangi bir değeri değişkene atmadan önce ifadeyi unicode'a çevirir. Böylece, uygulamanızda açıkça yazılmamış bir şeyi hiçbir zaman enjekte edememenizi sağlar. İşlenmeden önce her şey bir string'e dönüştürülür. Bu, XSS saldırılarını önlemeye yardımcı olur.
Örneğin &
ifadesi &
, <
ifadesi <
, >
ifadesi >
, "
ifadesi "
, '
ifadesi '
şekline dönüşecektir. Böylece input üzerinden XSS saldırısı yapmaya kalkılsa bile yazılan kod tamamen string'e dönüşmüş olduğundan etkisiz olacaktır.
Babel, JSX'i React.createElement()
çağrılarına derlemektedir.
Yani bizim yazdığımız tüm JSX ifadeleri Babel tarafından derlenerek saf JavaScript'in anlayacağı şekilde objelere dönüşür.
Bu iki örnek aynıdır:
const element = (
<h1 className='greeting'>
Merhaba Dünya!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Merhaba Dünya!'
);
React.createElement()
hatasız kod yazmanıza yardımcı olmak için birkaç kontrol gerçekleştirir ancak aslında böyle bir nesne oluşturur:
// Not: Bu yapı basitleştirilmiştir
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Merhaba Dünya!'
}
};
Bu nesneler React elementleri olarak adlandırılır. Bunları, ekranda görmek istediğiniz şeyin açıklaması olarak düşünebilirsiniz. React bu nesneleri okur, onları DOM'u oluşturmak ve güncel tutmak için kullanır.
Elementler React uygulamalarının en küçük yapı taşlarıdır.
Bir element, ekranda görmek istediğiniz şeyi tanımlar:
const element = <h1>Merhaba Dünya</h1>;
Tarayıcı DOM elementlerinden farklı olarak, React elementleri düz nesnelerdir ve oluşturulması kolaydır. React DOM, React elementleriyle eşleşecek şekilde DOM'u güncellemekle ilgilenir.
Bu konu hakkında detaylı Türkçe bilgi için Fatih Acet'in makalesine göz atabilirsiniz.
Not:
Daha yaygın olarak bilinen component kavramıyla elementler karıştırılabilir. Bir sonraki bölümde componentleri tanıtacağız.
Elementleri birer değişken, componentleri ise fonksiyon olarak düşünebilirsiniz; birbirinden farklı şeylerdir.
HTML dosyanızda bir <div>
olduğunu varsayalım:
<div id="root"></div>
Buna root DOM düğümü diyoruz çünkü içindeki her şey React DOM tarafından yönetilecektir.
DOM düğümü hakkında detaylı bilgi için JavaScript HTML DOM Navigation içeriğini inceleyenilirsiniz.
Sadece React ile oluşturulmuş uygulamalar genellikle tek bir root DOM düğümüne sahiptir.
React'i mevcut bir uygulamaya entegre ediyorsanız, istediğiniz kadar çok sayıda ayrı root DOM düğümü olabilir.
React elementini bir root DOM düğümüne dönüştürmek için, her ikisini de ReactDOM.render()
işlevine yerleştirin.
const element = <h1>Merhaba Dünya!</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
Sayfanızda "Merhaba Dünya!" başlığı görüntülenecektir.
React elementleri değişmez. Bir element oluşturduktan sonra, elementin alt elementlerini veya attributelerini değiştiremezsiniz.
Konu hakkında detaylı bilgi için Onur Aykaç'ın yazdığı makalede Javascript’te immutable ve mutable kavramı bölümünü okuyabilirsiniz.
Bildiğimiz kadarıyla UI'ı güncellemenin tek yolu yeni bir element oluşturmak ve ReactDOM.render()
fonksiyonuna geçirmektir.
Aşağıdaki saat örneğini inceleyelim.
function tick() {
const element = (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {new Date().toLocaleTimeString()}</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
ReactDOM.render()
fonksiyonunu her saniye setInterval() içerisinden çağırır.
Not:
Pratikte React uygulamalarının çoğu, bir kez
ReactDOM.render()
elementini çağırır.Birbiriyle bağlantı kurduğu için konuları atlamadan okumaya devam edin.
Yukarıdaki saat örneğinde setInterval
fonksiyonu sayesinde her saniye fonksiyon çağırılır ve ReactDOM render edilir.
Fakat pratikte bu kodlar bu şekilde yazılmıyor. ReactDOM bir kez render edilir ve oluşturulan elementler state
ler yardımıyla güncellenir.
İlerleyen konularda bunlara değineceğiz.
React DOM, componenti ve child (çocuk) componentleri önceki componentle karşılaştırır ve DOM'u istenen duruma getirmek için gereken yerlerde DOM güncellemelerini uygular.
Yani, React DOM tüm componentleri yeniden yüklemez. Değişiklik olan yeri algılar ve DOM üzerinde sadece o kısmı günceller, buda performansı olumlu yönde etkiler.
Tarayıcı üzerinden bu örneği inceleyerek doğrulayabilirsiniz.
Componentler, uygulamanızı tekrar kullanılabilir parçalara ayırmanıza ve her bir parçayı ayrı ayrı düşünmenize izin verir.
Kavramsal olarak, componentler JavaScript fonksiyonlarına benzemektedir.
Bunlar rasgele girdileri (props
olarak adlandırılır) kabul eder ve ekranda neler görüneceğini açıklayan React elementleri return ederler.
Sitenizi büyük bir puzzle olarak düşünün. React ile önce teker teker puzzle parçalarını oluşturup ardından bunları birleştirerek büyük resmi oluşturacaksınız.
Bir componenti tanımlamanın en basit yolu, bir JavaScript fonksiyonu yazmaktır:
function Welcome(props) {
return <h1>Merhaba {props.name}</h1>;
}
Bu fonksiyon geçerli bir React componenti olduğundan, tek bir props
parametresini obje olarak alır ve bir React componenti return eder.
Bu componenti "fonksiyonel" olarak adlandırırız çünkü tam anlamıyla JavaScript fonksiyonudur.
Bir componenti tanımlamak için bir ES6 sınıfı da kullanabilirsiniz: (ES6 hakkında detaylar eklenecektir.)
class Welcome extends React.Component {
render() {
return <h1>Merhaba {this.props.name}</h1>;
}
}
Yukarıdaki iki component, React'in bakış açısından eşittir.
Class'ların sonraki bölümlerde göreceğimiz bazı ek özellikleri var. O zamana kadar componentleri en saf hallerini kullanacağız.
İlerleyen konularda class'lar içerisine constructor, props, state ve çeşitli fonksiyonlar dahil olacak. Fakat konuyu dağıtmamak adına class'ları yukarıdaki en saf halleriyle kullanmaya devam edelim.
Daha önce yalnızca DOM etiketlerini temsil eden React elementleriyle karşılaştık:
const element = <div />;
Bununla birlikte, elementler componentleri de temsil edebilir:
const element = <Welcome name="Ömer" />;
React, kullanıcı tanımlı bir componenti temsil eden bir element görürse, JSX attributelerini tek bir obje olarak bu componente aktarır. Bu objeye props
diyoruz.
Yani bir componente <Welcome name="Ömer" surname="Gülçiçek" job="Yazılım Mühendisi" />
gibi
birden çok attribute eklediğimiz zaman, React bunları aşağıdaki gibi tek bir objede toplar, buna props
denir.
Ardından bu props objesini Welcome
componentine parametre olarak gönderir.
Hosgeldin
componentinde props.name
kullanım şekli ile "Ömer" değerini çekebiliriz.
<Welcome name="Ömer" surname="Gülçiçek" job="Yazılım Mühendisi" />
props = {
name: "Ömer",
surname: "Gülçiçek",
job: "Yazılım Mühendisi"
}
Örneğin, bu kod sayfaya "Merhaba Ömer" başlığını yazar:
function Welcome(props) {
return <h1>Merhaba {props.name}</h1>;
}
const element = <Welcome name="Ömer" />;
ReactDOM.render(
element,
document.getElementById('root')
);
Bu örnekteki olayları aşama aşama inceleyelim:
-
ReactDOM.render()
fonksiyonundan<Welcome name='Ömer' />
elementini çağırırız. -
React,
Welcome
componentine parametre olarak{name: 'Ömer'}
objesini alır. -
Welcome
componenti<h1>Merhaba Ömer</h1>
elementini return eder. -
React DOM, DOM'u
<h1>Merhaba Ömer</h1>
olacak şekilde günceller.
Uyarı:
Component adlarını daima büyük harfle başlatın.
Örneğin,
<div />
bir DOM etiketini temsil eder ama<Welcome />
bir componenti temsil eder.
Component çıktılarında başka componentlere başvurabilir. Bu, herhangi bir componenti farklı şekillerde kullanmamızı sağlar. Bir buton, bir form, bir diyalog, bir ekran: React uygulamalarında, hepsi genel olarak component olarak ifade edilir.
Örneğin, Welcome
'i birçok kez çağıran bir App
componenti oluşturabiliriz:
function Welcome(props) {
return <h1>Merhaba {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Ömer" />
<Welcome name="Muhammed" />
<Welcome name="Burak" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Genelde, React uygulamalarında tek bir App
componenti çağrılır.
Bununla birlikte, React'ı varolan bir uygulamaya entegre ederseniz,
Buton
gibi küçük bir component ile iç componentten dışa doğru kodlamaya başlayabilir ve görünüm hiyerarşisinin en üst noktasına yavaş yavaş ilerleyebilirsiniz.
Bu örnekteki olaylarıda aşama aşama inceleyelim:
-
ReactDOM.render()
ilk olarak<App />
componentini çağırdı. -
<App />
componenti ise 3 tane<Welcome />
componenti return edecek. Fakat<Welcome />
componentine parametre olarak farklı objeler yollanmış. -
İlk
<Welcome />
componenti ekranaMerhaba Ömer
yazacak. Ardından tekrar<Welcome />
componenti çağrılacak fakat bu sefer objedeki ad Muhammed, son olarakta Burak olacak. -
Sonuç olarak ekranda bir
div
içerisindeMerhaba Ömer
,Merhaba Muhammed
veMerhaba Burak
yazan başlıklar yazdırılacaktır.
Componentleri daha küçük componentleri bölmekten korkmayın.
Örneğin, bu Yorum
componentini düşünün:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
author
(obje), text
(string) ve date
(date) props olarak kabul eder ve bir yorum divi return eder.
Bu component, iç içe birçok componenti olduğu için değiştirilmesi zor olabilir, bu yüzden componenti parçalayarak küçük componentler oluşturalım.
İlk olarak Avatar
componentini oluşturalım:
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
Ardından Comment
componentinin içerisinden, yeni oluşturduğumuz Avatar
componentini çağıralım.
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
Ardından, UserInfo
componentini ayıklayacağız:
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
Şimdide Comment
componentinin nasıl görüldüğüne bakalım.
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
Componentleri küçük parçalara bölmek başlangıçta gereksiz bir iş ya da zaman kaybı gibi gözükebilir, ancak daha büyük uygulamalarda tekrar kullanılabilir component paletine sahip olmak önemlidir.
Bir component (fonksiyon veya class) oluşturduğunuzda, kendi propslarını hiçbir zaman değiştirmemelisiniz.
Bir sum
fonksiyonu düşünün:
function sum(a, b) {
return a + b;
}
Bu fonksiyonlara pure
(saf) denir çünkü parametrelerini değiştirmeye çalışmazlar ve aynı parametreler için aynı sonuca her zaman return edilir.
Buna karşın, bu fonksiyon kendi parametresini değiştirdiği için saf değildir:
function withdraw(account, amount) {
account.total -= amount;
}
React oldukça esnektir fakat tek bir katı kuralı vardır:
Tüm React componentleri, propslar ile ilgili olarak saf fonksiyonlar gibi hareket etmelidir.
Elbette uygulama arayüzü dinamiktir ve zaman içinde değişecektir.
Bir sonraki bölümde state
kavramı tanıtılacaktır.
State, React componentlerine, kullanıcı eylemlerine ve diğer herhangi bir şeye bu kuralı ihlal etmeden değerlerin değiştirmesine izin verir.
Özet olarak, propsları asla değiştirmeye kalkmıyoruz. Propslar sadece okunabilir. Bir sonraki bölümde state kavramı işlenecektir. Propslar state'lerden değeri çekecek, değeri değiştirmek istediğimizde state'i değiştireceğiz. Haliyle propsta değişmiş olacak.
Önceki konularda gördüğümüz saat örneğini düşünün.
Şu ana kadar UI'ı güncellemenin tek bir yolunu öğrendik.
Görüntülenen çıktıyı değiştirmek için ReactDOM.render()
fonksiyonunu her saniye çağırıyorduk:
function tick() {
const element = (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {new Date().toLocaleTimeString()}</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
Bu bölümde, Clock
componentini yeniden kullanımının doğru yöntemini öğreneceğiz.
Zamanlayıcıyı kendisi ayarlayacak ve her saniyede bir güncelleme yapacaktır.
Clock
componentinin nasıl göründüğünü yazmakla başlayabiliriz:
function Clock(props) {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {props.date.toLocaleTimeString()}</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
Daha önce anlatılmıştı fakat tekrar etmekte fayda var.
Clock
componenti çağrılırken attribute olarak date={new Date()}
gönderiliyor.
Bu attribute, Clock
componentine parametre olarak gelir ve adı props
tur.
Clock
componentine bu tarihi yazdırmak içinde props.date
şeklinde kullanıyoruz.
Bunu bir kez yazmak ve Clock
component güncellemesini kendisi gerçekleştirsin isteriz:
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Bunu uygulamak için Clock
componentine state
eklemeliyiz.
Stateler propslara benzer, ancak tamamen component tarafından kontrol edilir.
Daha önce belirttiğimiz gibi, class olarak tanımlanan componentlerin bazı ilave özellikler var.
State yalnızca classlar için kullanılabilen bir özelliktir.
Bu kısıma kadar componentleri fonksiyon olarak tanımlamıştık. Fakat React'ta class olarak oluşturmaya alışmak daha doğru olacaktır. Şimdi adım adım bir fonksiyonun class'a çevrilme işlemini inceleyelim.
Clock
fonksiyon componentini aşağıdaki adımlarla class componentine dönüştürebiliriz.
-
React.Component
ine extend eden aynı ada sahip bir ES6 classı oluşturalım. (class Clock extends React.Component
) -
Buna
render()
adı verilen boş bir fonksiyon ekleyin. -
Fonksiyonun içerisindeki kodları
render()
ın içerisine taşıyın. -
props
un adınıthis.props
olarak değiştirin.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.props.date.toLocaleTimeString()}/h2>
</div>
);
}
}
Fonksiyon component yerine class component kullanımına alışın. Üst kısımdaki adımları anlamadıysanız tekrar tekrar okumanızda fayda var.
Clock
componenti bir fonksiyon yerine bir class olarak tanımlanmış oldu.
Bu, state
ve lifecycle
(yaşam döngüsü) gibi ek özellikleri kullanmamızı sağlar.
Bir class'a state eklemek için aşağıdaki 3 adımı dikkatlice inceleyiniz.
this.props.date
satırınıthis.state.date
olarak güncelleyin:
class Clock extends React.Component {
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
- Class componentimizin içerisine state'i ilk anda tanımlayan bir constructor oluşturalım.
Constructor fonksiyonu, bir classla beraber oluşturulan, nesnenin oluşturulması ve başlatılması için özel bir fonskiyondur. Bir classta "constructor" ismiyle yalnızca bir tane özel fonskiyon olabilir. Class oluşturulduğu anda içerisindeki kodlar çalışır.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
props
un constructorden nasıl alındığına dikkat ediniz:
constructor
e parametre olarak props
ekleyip, içerisine super(props);
satırını ekliyoruz.
constructor(props) {
super(props);
this.state = {date: new Date()};
}
Class componenleri her zaman constructore props
ile çağırmalıdır.
<Clock />
componentinden date
attribute'ünü kaldırın:
ReactDOM.render(
//eski hali = <Clock date={new Date()} />
<Clock />,
document.getElementById('root')
);
Zamanlayıcı kodunu daha sonra componentin kendisine geri ekleyeceğiz.
Şu anda sonuç şöyle görünüyor:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Şimdi, Clock
i kendi zamanlayıcısını oluşturup her saniyede bir güncelleyeceğiz.
Birçok componente sahip uygulamalarda, componentler yok edildiğinde alınan kaynakları boşaltmak çok önemlidir.
Clock
, DOM'a ilk defa çağırıldığında bir zamanlayıcı ayarlamak istiyoruz. Buna React'te mounting
denir.
Ayrıca, Saat componentinden üretilen DOM kaldırıldığında,
bu zamanlayıcıyı temizlemek istiyoruz.
Buna ise React'te unmounting
denir.
Bir component DOM'a yazdırılıp kaldırıldığında bazı kod satırlarını çalıştırmak için class componente özel yöntemler ekleyebiliriz:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
//Component oluşturulduğunda çalışacak fonksiyon
}
componentWillUnmount() {
//Component kaldırıldığında çalışacak fonksiyon
}
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
Bu fonksiyonlara lifecycle hooks
(yaşam döngüsü kancaları) adı verilir.
Component çıktısı DOM'a aktarıldıktan sonra componentDidMount()
fonksiyonu çalışır.
Bu, zamanlayıcı ayarlamak için iyi bir yerdir:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
() => this.tick()
kullanımı ES6
ile gelen kısa fonksiyon kullanımıdır.
ES5
'teki kullanımı function() { return this.tick();
}
Zamanlayıcı this
in üzerine nasıl kaydettiğimizi unutmayın.
Zamanlayıcıyı componentWillUnmount()
fonksiyonunda temizleyeceğiz:
componentWillUnmount() {
clearInterval(this.timerID);
}
Son olarak, Clock
componentini her saniyede bir çalışacağı tick()
adında bir fonksiyon oluşturacağız.
Bu fonksiyon state'eki tarihi this.setState()
kullanarak güncelleyecektir.
Daha önceki konularda props
ların yalnızca okunabilir olduğunu ve güncellenmemesi gerektiğinin notunu düşmüştük.
Bizler (this.setState()
ile) state
'i değiştireceğiz, React ise o state'i kullanan yerleri (props
ları) kendisi güncelleyecek.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
//Fonksiyon tanımlarken başına "function" yazmadığımıza dikkat edin.
//React componentleri içerisinde fonksiyon tanımlamak istediğimizde direkt isim vererek yazıyoruz.
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Neler olup bittiğini hızla özetleyelim:
-
ReactDOM.render()
içerisinde<Clock />
componenti çağırıldığında, ReactClock
componentinin constructorünü çağırır.Clock
componentinin geçerli saati göstermesi gerektiği içinthis.state
'i geçerli saat ile başlatır. Daha sonra bu state güncellenecek. -
React sonra
Clock
componentininrender()
fonksiyonunu çağırır. React, ekranda neyin görüntülenmesi gerektiğini öğrenir. Daha sonra,Clock
componentinin çıktısı ile eşleşecek şekilde DOM'u güncelleştirir. -
Clock
componentinin çıktısı DOM'a eklendiğinde, React,componentDidMount()
fonksiyonunu çağırır. Her saniye çalışacak olantick()
fonksiyonunutimerID
de tutar. -
Her saniye tarayıcı
tick()
fonksiyonunu çağırır.Clock
componentinin içinde, geçerli saati içeren bir nesneylesetState()
i çağırarak bir UI güncellemesi içinsetInterval()
fonksiyonunu hazırlar.setState()
çağrısı sayesinde React, statin değiştiğini bilir ve ekranda ne olması gerektiğini öğrenmek için tekrarrender()
eder. Bu seferrender()
yöntemindekithis.state.date
güncellenmiş olacaktır. React DOM'u buna göre günceller. -
Clock
componenti DOM'dan kaldırıldıysa, React,timerID
durduğu andacomponentWillUnmount()
fonksiyonunu çağırır.
setState()
hakkında bilmeniz gereken üç şey vardır.
Örneğin, bu bir componenti yeniden oluşturmaz:
// Yanlış Kullanım
this.state.comment = 'Merhaba';
setState()
i şu şekilde kullanın:
// Doğru Kullanım
this.setState({comment: 'Merhaba'});
this.state
i atayabileceğiniz tek yer constructor
dür.
React, birden fazla setState()
çağrısını performans için tek bir güncelleme haline getirebilir.
Çünkü this.props
ve this.state
eşzamansız olarak güncellenebilir.
Örneğin, bu sayaç güncellemesi başarısız olabilir:
// Yanlış Kullanım
this.setState({
counter: this.state.counter + this.props.increment,
});
Bunu düzeltmek için, bir objeden ziyade bir fonksiyonu kabul eden ikinci bir setState()
formunu kullanın.
Bu işlev önceki durumu ilk argüman olarak, güncelleme ikinci argüman olarak uygulandığında sahne alacaktır:
// Doğru Kullanım
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
Yukarıdaki örnekte ES6 ile gelen ok fonksiyonu kullandık fakat normal fonksiyonlarla da yapabilirdik:
// Doğru Kullanım
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
setState()
öğesini çağırdığınızda, React state'i birleştirir. Bu yüzden birden fazla state aynı anda güncellenebilir.
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
Ayrıca bunları ayrı setState()
çağrılarıyla bağımsız olarakta güncelleyebilirsiniz:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
setState
i kullandığımızda tüm state'ler değiştirilmez. Yalnızca belirtilen state güncellenir.
Yani this.setState({comments})
şeklinde kullanıldığında sadece this.state.comments
güncellenir, this.state.posts
güncellenmez.
React elementleri ile click ve change gibi olayları izlemek, DOM elementlerindeki olayları işlemekle çok benzerdir.
Click, change gibi olaylara event
denir.
Tam liste için tıklayınız.
Daha akılda kalıcı olması ve ağırlıklı olarak click
ve change
olayları kullanıldığı için
başlıktada event olayları
yerine click ve change olayları
başlığını kullandım.
Bazı syntax farklılıkları vardır:
- React eventleri, küçük harf yerine
camelCase
kullanılarak isimlendirilir.
camelCase'den daha öncede bahsetmiştik.
İlk kelime hariç diğer kelimelerin ilk harfleri büyük ve birleşik yazılır. Arada - (tire) kullanılmaz.
React'te class
yerine className
, tabindex
yerine tabIndex
, onclick
yerine onClick
, onchange
yerine onChange
kullanılır.)
- JSX ile bir string yerine event işleyicisi olarak bir fonksiyon iletirsiniz.
Örneğin, HTML'de:
<button onclick="activateLasers()">
Linki aktifleştir
</button>
React'te ise biraz farklıdır:
<button onClick={activateLasers}>
Linki aktifleştir
</button>
Başka bir fark ise React'te varsayılan davranışı engellemek için false
return etmektir.
preventDefault
'u açıkça çağırmalısınız.
Örneğin, düz HTML'de yeni bir sayfayı açmanın varsayılan bağlantı davranışını önlemek için şunları yazabilirsiniz:
<a href="#" onclick="console.log('Bağlantıya tıklandı.'); return false">
Tıkla
</a>
React'te bunun yerine:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('Bağlantıya tıklandı.');
}
return (
<a href="#" onClick={handleClick}>
Tıkla
</a>
);
}
Burada, e
sentetik bir olaydır.
React, bu sentetik olayları W3C spesifikasyonuna göre tanımlar;
dolayısıyla tarayıcılar arası uyumluluk konusunda endişelenmenize gerek yoktur.
React'i kullanırken genellikle bir DOM elementi oluşturulduktan sonra, o elementin click eventini izlemek için
addEventListener
i çağırmamamız gerekir.
Bunun yerine, element başlangıçta oluşturulduğunda bir click izleyicisi oluşturun.
Bir componenti ES6 classı kullanarak tanımladığınızda, ortak bir desen, bir olay işleyicisinin classtaki bir fonksiyon olmasını sağlar. Örneğin, bu Toggle
componenti, kullanıcının "Açık" ve "Kapalı" durumları arasında geçiş yapmasını sağlayan bir buton oluşturur:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// Click, change gibi olayların çalışabilmesi için aşağıdaki gibi bind etmek gerekir.
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'Açık' : 'Kapalı'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
Bind etmek ile ilgili detaylı bilgi için kendi yazığım bind fonksiyonu adlı makaleyi okuyabilirsiniz.
JSX geriçağırımlarında bu konuda dikkatli olmalısınız. JavaScript'te, class fonskyionları varsayılan olarak bağlı değildir.
this.handleClick
'i bind
etmeyi (bağlamayı) unutursanız ve onClick
e iletirseniz, fonksiyon çağrıldığında bu undefined
olur.
Bu, React'e özgü davranış değildir; fonksiyonların JavaScript'te nasıl işlediğinin bir parçasıdır.
Genellikle, onClick = {this.handleClick} gibi bir yöntemin arkasından ()
olmadan işaret ederseniz, bu yöntemi bind etmeniz gerekir.
Bu şekilde bind etmek istemiyorsanız, iki farklı yolu daha vardır.
class LoggingButton extends React.Component {
// Bu syntax `this`'nin clickOlayi içinde bağlanmasını sağlar.
// Uyarı: Bu *deneysel* bir syntaxtır.
handleClick = () => {
console.log(this);
}
render() {
return (
<button onClick={this.handleClick}>
Tıkla
</button>
);
}
}
Bu syntax Create React App'te varsayılan olarak etkindir.
Return işleminde bir ok fonksiyonu kullanabilirsiniz:
class LoggingButton extends React.Component {
handleClick() {
console.log(this);
}
render() {
// Bu syntax `this`'in bind edilmesini sağlar.
return (
<button onClick={(e) => this.handleClick(e)}>
Tıkla
</button>
);
}
}
Bu syntax ile ilgili sorun, LoggingButton
un her işleyişinde farklı bir callback oluşturulmasıdır.
Çoğu durumda, bu iyidir.
Bununla birlikte bu callback, alt componentlere props
olarak iletilirse, bu componentler ek bir yeniden oluşturma işleyebilir.
Genellikle, constructorde bind etmenizi öneririz.
Bir döngü içinde, bir event olayına fazladan bir parametre göndermek isteyebilirsiniz.
Örneğin, id
satır kimliğiyse, aşağıdakilerden herhangi biri işe yarayabilir:
<button onClick={(e) => this.deleteRow(id, e)}>Satırı Sil</button>
<button onClick={this.deleteRow.bind(this, id)}>Satırı Sil</button>
Yukarıdaki iki satır eşittir, ok fonksiyonlarını ve Function.prototype.bindı kullanır.
Her iki durumda da, React olayını temsil eden e
argümanı id'den sonra ikinci bir parametre olarak aktarılır.
Bir ok fonskiyonu ile bunu açıkça belirtmeliyiz, ancak bind
ile başka argümanlar otomatik olarak iletilir.
React ile ihtiyacınız olan davranışı kapsayan farklı componentler oluşturabilirsiniz. Daha sonra, uygulamanızın state'ine bağlı olarak yalnızca bir kısmını görüntüleyebilirsiniz.
React'te şartlı render, JavaScript koşulları ile aynı şekilde çalışır.
Geçerli durumu temsil eden elemanlar oluşturmak için if
veya koşullu operatör gibi JavaScript operatörlerini kullanın
ve bunları eşleştirmek için UI'ı güncelleştirmeye izin verin.
Bu iki componenti düşünün:
function UserGreeting(props) {
return <h1>Tekrardan hoşgeldin!</h1>;
}
function GuestGreeting(props) {
return <h1>Lütfen üye ol.</h1>;
}
Bir kullanıcının oturum açıp açmadığına bağlı olarak bu componentlerin herhangi birini görüntüleyen bir
Greeting
componenti oluşturacağız:
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// isLoggedIn={true} olarak değiştirip farkı test edebilirsiniz.
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
Adım adım incelemekte fayda var.
-
İlk olarak ReactDOM,
Greeting
componentini render ediyor. -
Farkettiğiniz gibi componentin içerisinden props olacak bir
isLoggedIn={false}
değerini gönderiyor. -
Greeting
componentinde, parametre olarak gönderilenboolean
(true ya da false) değeri bir değişkene atıyor. -
Ardından bu değer true ise
UserGreeting
componentini, false iseGuestGreeting
componentini return ediyor.
Elementleri depolamak için değişkenleri kullanabilirsiniz.
Bu, koşullu olarak componentin bir bölümünü oluşturmanıza yardımcı olabilir, ancak çıktının geri kalanı değişmez.
LoginButton
ve LogoutButton
düğmelerini temsil eden bu iki yeni componenti inceleyelim:
function LoginButton(props) {
return (
<button onClick={props.onClick}>
Giriş Yap
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
Çıkış Yap
</button>
);
}
Aşağıdaki örnekte, Greeting
adı verilen state'i olan component oluşturacağız.
Geçerli durumuna bağlı olarak <LoginButton />
veya <LogoutButton />
işlevlerini oluşturacaktır:
class Greeting extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<UserGreeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
ReactDOM.render(
<Greeting />,
document.getElementById('root')
);
Bir değişkeni bildirmek ve if
ifadesi kullanmak koşullu olarak bir componenti oluşturmak için iyi bir yoldur,
bazen daha kısa bir syntax kullanmak isteyebilirsiniz.
Aşağıda açıklanan, JSX'de koşulları sıralamanın birkaç yolu vardır.
Herhangi bir ifadeyi JSX'e süslü parantez içine sararak gömebilirsiniz.
Buna JavaScript mantıksal &&
operatörü dahildir.
Koşullu olarak bir elementi dahil etmek için kullanışlı olabilir:
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Merhaba!</h1>
{unreadMessages.length > 0 &&
<h2>
{unreadMessages.length} adet okunmamış mesajınız bulunmaktadır.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
JavaScript true && ifade
olduğunda burada ifade
kısmında yazacağımız kodu çalıştırır.
Yani &&
nin sol tarafı true ise, sağ tarafını çalıştırır.
false && ifade
durumunda ise false
olarak değerlendirir.
Kısa if-else kullanımda x = (x == 1) ? ++x
şeklinde kullanım yapamıyoruz.
Mutlaka else
durumunuda yazmamız gerekiyor. Yani şu şekilde: x = (x == 1) ? ++x : --x
Fakat && operatörü ile else durumu olmadan kısa yoldan sadece if
kontrolü yapmış oluyoruz.
Koşullu olarak oluşturma yöntemi için bir başka yöntem de JavaScript if-else operatörünü kullanmaktır.
ifade ? dogru : yanlis
Bu kullanım if { } else { }'in kısa kullanımıdır.
ifade
olarak belirtilen yer eğer true ise dogru
yazan yerdeki kod çalışır,
aksi taktirde ise yanlis
yazan yerdeki kod çalışır.
Aşağıdaki örnekte, if-else koşulunu küçük bir metin bloğu oluşturmak için kullanıyoruz.
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
Kullanıcı siteye giriş <b>{isLoggedIn ? 'yaptı' : 'yapmadı'}</b>.
</div>
);
}
Bununla birlikte daha büyük ifadeler için de kullanılabilir, ancak kod karmaşıklaşır:
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
Tıpkı JavaScript'te olduğu gibi, siz ve ekibiniz hangisinin daha okunabilir olduğunu düşünüyorsa o yöntemi kullanabilir.
Nadiren de olsa, bir component, başka bir component tarafından oluşturulmuş olsa bile gizlenmesini isteyebilir.
Bunu yapmak için null
return etmek gereklidir.
Aşağıdaki örnekte, <WarningBanner />
componenti, warn
adlı props değerine bağlı olarak oluşturulmuştur.
Props değeri false
ise, component oluşturmaz:
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Uyarı!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true}
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(prevState => ({
showWarning: !prevState.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Gizle' : 'Göster'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
Bir componenti null
olarak render
yöntemiyle return etmek, componentin lifecycle fonksiyonlarının başlatılmasını etkilemez.
Örneğin, componentWillUpdate
ve componentDidUpdate
yine de çağrılacaktır.
Önce, JavaScript'te listeleri nasıl dönüştürdüğümüzü gözden geçirelim.
Aşağıdaki kod göz önüne alındığında, bir dizi sayı almak ve değerlerini ikiye katlamak için map()
fonksiyonunu kullanıyoruz.
map()
tarafından return edilen çift katlı değerleri kaydederiz:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
Konsola [2, 4, 6, 8, 10]
yazacaktır.
Google Chrome'da (diğer tarayıcılarda da benzer ya da aynıdır) F12'ye basıp, "Console (yada Konsol)" sekmesine gelip bu kodları yapıştırarak test edebilirsiniz.
Ok fonksiyonları ile ilgili dokümantasyon hazırlandığında bu kısım güncellenecektir.
Kısaca özet geçmek gerekirse yukarıda map fonksiyonunun içerisinde yapılan işlem ((number) => number * 2
) şudur:
number
parametre alınır ve number*2
return edilir.
Fonksiyonun kolay okunabilir hali ise şudur:
function(number) {
return number*2;
}
React'te, dizi elemanlarını listelere dönüştürmek neredeyse aynıdır.
Koleksiyonları oluşturabilir ve bunları JSX içine süslü parantez {}
kullanarak ekleyebilirsiniz.
Aşağıda, JavaScript map()
fonksiyonu kullanarak numbers dizisinde döngü oluşturuyoruz.
Her liste için bir <li>
return ediyoruz. Son olarak, elde edilen sonuç dizisini listItems
e atayacağız:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
Bütün listItems
dizesini <ul>
elemanının içerisine dahil ediyoruz ve bunu DOM'a gönderiyoruz:
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
Bu kod, ekrana ul
kapsayıcısı ve li
ler içerisinde rakamları yazacaktır.
Genellikle listeleri bir component içinde render ederiz.
Önceki örneği, numbers
adında bir dizi kabul eden ve liste çıktısı yapan bir componente dönüştürebiliriz.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Bu kodu çalıştırdığınızda, liste için bir key
(anahtar) verilmesi yönünde bir uyarı verilir.
Burada key kelimesine (Türkçe'si anahtar demek olmasına rağmen) anahtar çevirisi tam olarak uymuyor. Key denirken aslında benzersizi ifade etmektedir (ID, Kimlik numarası, parmak izi gibi).
Bir key
, listeleri oluşturulurken eklenmesi gereken özel bir attribute'tür.
Bir sonraki bölümde bunun neden önemli olduğunu anlatacağız.
numbers.map()
içindeki listelere birer key
atayalım.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Key
ler yardımı ile hangi listenin değiştiğini, eklendiğini veya kaldırıldığını belirleyebiliriz.
Elemanlara istikrarlı bir kimlik kazandırmak için dizideki listelere key
verilmelidir:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
Bir key
seçmenin en iyi yolu, kardeşleri (diğer li
ler) arasında bir tanımlanan bir kimlik kullanmaktır.
Çoğu zaman, verilerinizdeki id'ler key
olarak kullanılır:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
Eğer bu tarzda bir yapınız yoksa benzersiz olacağı için key
lere index
verebiliriz:
const todoItems = todos.map((todo, index) =>
// Listelerin benzersiz id'leri yoksa bu yöntemi kullanın.
<li key={index}>
{todo.text}
</li>
);
Listelerin sırası değişebilirse keyler için index kullanmanızı önermiyoruz. Bu, performansı olumsuz etkileyebilir ve component state'i ile ilgili sorunlara neden olabilir.
Ayrıntılı bilgi için Robin Pokorny'nin makalesini inceleyebilirsiniz (İngilizce).
Listelere siz key atamazsanız React varsayılan olarak index'i atar.
Dizilerde kullanılan keyler kardeşleri arasında benzersiz olmalıdır. Bununla birlikte, global olarak benzersiz olması gerekmez.
İki farklı diziyi üretirken aynı keyleri kullanabiliriz:
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'Merhaba Dünya', content: 'React Öğreniyoruz!'},
{id: 2, title: 'Ömer Gülçiçek', content: 'React JS Türkçe Dokümantasyon'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('root')
);
Keyler React'te bir ipucu işlevi görür ancak componentlere aktarılmazlar. Componentlerde aynı değere ihtiyacınız varsa, açıkça farklı bir ada sahip bir props olarak iletebilirsiniz:
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
Yukarıdaki örnekte, Post
componenti props.id
yi okuyabilir, ancak props.key
i okuyamaz.
Yukarıdaki örneklerde hep farklı bir listItems
değişkeni tanımlayıp ve JSX'e dahil ettik:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
JSX, herhangi bir ifadeyi süslü parantez içine yerleştirmeye izin verir, böylece map()
fonksiyonu ile sonucu sıralayabiliriz:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
Bazen bu daha temiz koda neden olur, ancak bu stil de istismar edilebilir. JavaScript'te olduğu gibi okunabilirlik açısından bir değişkenin çıkartılmaya değer olup olmadığına karar vermek size aittir. Kod çok iç içe geçmişse, bir componenti ayıklamak için doğru zamanın geldiğini aklınızda bulundurun.
HTML form elemanları, React'te diğer DOM elemanlarından biraz farklı çalışır, çünkü form elemanlarının kendilerine has iç stateleri vardır.
Örneğin, bu kod HTML'de bir form içerisinde name
girişi ister:
<form>
<label>
Adınız:
<input type="text" name="name" />
</label>
<input type="submit" value="Gönder" />
</form>
HTML'de, <input>
, <textarea>
ve <select>
gibi form elemanları genellikle kendi state'ini korur ve kullanıcı girdisine dayalı olarak güncelleşir.
React'te ise state'ler genellikle componentlerin this.state
özelliğinde saklanır ve yalnızca setState()
ile güncellenir.
React state'te tek kaynak olarak ikisini birleştirebiliriz.
Ardından form oluşturan React componenti, sonraki kullanıcı girişi üzerinde bu formda olanı da kontrol eder.
Değeri React tarafından bu şekilde kontrol edilen bir giriş form elemanına kontrollü component
denir.
Örneğin, bir önceki örnekte, name
değerinin yazılıp submit edildiğinde name
ı alert ile yazdırmak istiyorsak,
formu kontrollü bir component olarak oluşturabiliriz:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Gönderilen değer: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Gönder" />
</form>
);
}
}
value
attribute'ü input'un kendisinde zaten var.
Öyleyse bu değeri almak için yeni bir React state'i oluşturmaya gerek yok.
Bu inputta value
olarak state'i yazdıracağız ve input'ta her değişiklik olduğunda bu state'i güncelleyeceğiz.
Kontrollü bir componentte her state değişimi, handleChange
fonksiyonunu çalıştıracaktır.
Örneğin, adın büyük harflerle yazılmasını isteseydik, handleChange
fonksiyonunu şu şekilde yazabilirdik:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
Buradaki event.target
input'un ta kendisidir.
Bunu fonksiyon içerisinde console.dir(event.target);
yazarak konsoldan doğrulayabilirsiniz.
Haliyle event.target.value
ise o inputtaki değeri alacaktır.
HTML'de, <textarea>
tagı yazıyı çocuğunda tanımlar:
Yani yazılan yazı input
ta olduğu gibi tagın kendisinde değil, tagın içerisindedir.
<textarea>
Merhaba, burası textarea yazı alanıdır.
</textarea>
Bunun yerine React, <textarea>
için bir value
attribute'ü kullanır.
Bu şekilde <textarea>
kullanan bir form, tek satırlı bir girdi kullanan bir forma çok benzer şekilde yazılabilir:
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Bu kısma bir şeyler yazınız.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Gönderilen değer: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Essay:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Gönder" />
</form>
);
}
}
this.state.value
in constructor'te başlatıldığına dikkat edin, böylece textarea
içerisinde varsayılan olarak bu yazı bulunacaktır.
HTML'de <select>
, bir açılır liste oluşturur. Örneğin, aşağıdaki kod bazı illeri listeler:
<select>
<option value="istanbul">İstanbul</option>
<option value="ankara">Ankara</option>
<option selected value="trabzon">Trabzon</option>
<option value="izmir">İzmir</option>
</select>
Trabzon
seçeneğinin başlangıçta selected
attribute'ü yüzünden seçili olarak geleceğini unutmayın.
React, bu selected
attribute'ünü kullanmak yerine, select
etiketinde bir value
attribute'ü kullanır.
Kontrollü bir componentte bu daha kullanışlıdır çünkü yalnızca bir yerde güncelleme yapmanızı sağlar. Örneğin:
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Trabzon'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Hangi ilde yaşamak isterdiniz: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
En sevdiğiniz il:
<select value={this.state.value} onChange={this.handleChange}>
<option value="istanbul">İstanbul</option>
<option value="ankara">Ankara</option>
<option value="trabzon">Trabzon</option>
<option value="izmir">İzmir</option>
</select>
</label>
<input type="submit" value="Gönder" />
</form>
);
}
}
Genel olarak bu, <input type="text">
, <textarea>
ve <select>
elementlerinin çok benzer şekilde çalışmasını sağlar.
Not
Bir
select
etiketinde birden fazla seçeneği seçmenize izin veren bir diziyivalue
attribute'üne yazabilirsiniz:<select multiple={true} value={['B', 'C']}>
HTML'de <input type="file">
aygıt depolama alanından bir veya daha fazla dosya seçmenizi sağlar.
<input type="file" />
React'te bir <input type="file" />
, önemli bir farkla normal bir <input />
a benzer şekilde çalışır: read-only'dir.
(Yani yalnızca okunabilirdir, props
lar gibi.)
Çoklu kontrollü input
öğelerini ele almanız gerektiğinde,
her öğeye bir name
özniteliği ekleyebilir ve işleyici işlevinin event.target.name
değerine dayanarak ne yapılacağını seçmesine izin verebilirsiniz.
Örneğin:
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Number of guests:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
Verilen girdi ismine karşılık gelen state keyini güncellemek için ES6 syntax'ını nasıl kullandığımıza dikkat edin:
this.setState({
[name]: value
});
Buda ES5'teki eşdeğer kodudur.
var partialState = {};
partialState[name] = value;
this.setState(partialState);
Kontrollü bir component üzerindeki props'u belirlemek, kullanıcının isteği dışında girişi değiştirmesini önler.
value
belirttiyseniz ancak girdi hala düzenlenebilir ise, yanlışlıkla value
i undefined
veya null
olarak ayarlamış olabilirsiniz.
Aşağıdaki kod bunu göstermektedir. (Giriş ilk önce kilitlenir ancak kısa bir gecikme sonrasında düzenlenebilir hale gelir.)
ReactDOM.render(<input value="merhaba" />, mountNode);
setTimeout(function() {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
Çoğu zaman, birden çok componentin aynı state'i yansıtması gerekir. Bu bölümde, suyun belirli bir sıcaklıkta kaynayıp kaynayamayacağını hesaplayan fonksiyonları oluşturacağız.
BoilingVerdict
adlı bir componentle başlayacağız. Celsius
sıcaklığını bir props ile parametre olarak kabul eder ve suyu kaynatmaya yetecek kadar olup olmadığını return eder:
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>Su kaynar.</p>;
}
return <p>Suyun kaynaması için yeterli sıcaklık değil.</p>;
}
Sonra, Calculator
adlı bir component oluşturacağız.
Sıcaklığı girmenizi sağlayıp, bu değeri this.state.temperature
'te tutacak:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
return (
<fieldset>
<legend>Celsius sıcaklığını giriniz:</legend>
<input value={temperature} onChange={this.handleChange} />
<BoilingVerdict celsius={parseFloat(temperature)} />
</fieldset>
);
}
}
Şimdi yapacağımız şey ise Celsius inputuna ek olarak, bir Fahrenheit inputu sağlamak.
TemperatureInput
componentinden devam edebiliriz.
Yeni bir scale
propsu ekleyeceğiz ki bunlar c
ya da f
olabilirler:
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>{scaleNames[scale]} sıcaklığını giriniz:</legend>
<input value={temperature} onChange={this.handleChange} />
</fieldset>
);
}
}
Artık iki ayrı sıcaklık girişi oluşturmak için Calculator
componentini şu şekilde değiştirebiliriz:
class Calculator extends React.Component {
render() {
return (
<div>
<TemperatureInput scale="c" />
<TemperatureInput scale="f" />
</div>
);
}
}
Şu anda iki inputumuz var, ancak sıcaklıkların birine rakam yazdığımızda diğeri güncellenmiyor. Bu bizim gereksinimimizle çelişiyor, onları senkronize etmeyi istiyoruz.
İlk olarak Celsius ve Fahrenheit'ı birbirine dönüştürmek için gerekli iki fonksiyonu yazacağız:
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
Bu iki fonksiyon, sıcaklıkları birbirine dönüştürür.
Şu anda, TemperatureInput
componenti bağımsız olarak değerlerini local state'te tutuyor:
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
// ...
Bununla birlikte, bu iki inputun birbiriyle senkronize edilmesini istiyoruz. Celsius inputu güncellendiğinde, Fahrenheit inputu dönüştürülen sıcaklığı göstermelidir; aynı zamanda tersi de çalışmalıdır.
React'ta state, ihtiyaç duyan componentlerin en yakın ortak atasına taşınarak gerçekleştirilir.
Bunun yerine, state'i TemperatureInput
den çıkaracak ve onu Calculator
içine taşıyacağız.
Bunun nasıl olduğunu adım adım işleyelim.
İlk olarak, TemperatureInput
componentinde this.state.temperature
öğesini this.props.temperature
olarak değiştirelim.
render() {
// Önceden: const temperature = this.state.temperature;
const temperature = this.props.temperature;
// ...
Propsların read-only (sadece okunabilir, değiştirilemez) olduğunu biliyoruz.
temperature
state'teyken, TemperatureInput
bunu değiştirmek için this.setState()
yi çağırabilir.
Şimdi, TemperatureInput
sıcaklığı güncellemek istediğinde, this.props.onTemperatureChange
fonksiyonu yardımı ile yapacak:
handleChange(e) {
// Önceden: this.setState({temperature: e.target.value});
this.props.onTemperatureChange(e.target.value);
// ...
Not:
temperature
veyaonTemperatureChange
adlarının özel bir anlamı yoktur. Onlara,value
veonChange
gibi isimler verdik, başka bir şey diyebilirdik.
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>{scaleNames[scale]} sıcaklığını giriniz:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
Şimdi Calculator
componentine geçelim.
Mevcut inputun temperature
ve scale
değerlerini statede tutarız.
Örneğin, Celsius'a 37 yazarsak, Calculator
componentinin state'i şöyle olacaktır:
{
temperature: '37',
scale: 'c'
}
Eğer daha sonra Fahrenheit'ı 212 olarak değişirsek Calculator
componenti şöyle olur:
{
temperature: '212',
scale: 'f'
}
Her ikisinin girdisinide tutabilirdik fakat gereksiz olurdu.
En son girilen girdinin değerini ve gösterdiği ölçeği tutmak yeterlidir.
Daha sonra temperature
ve scale
değerlerine bağlı olarak diğerinin değerini hesaplayabiliriz.
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}
Şu anda, hangi inputun düzenlediğiniz önemli değil, Calculator'daki this.state.temperature
ve this.state.scale
güncellenir. Inputlardan bir tanesi olduğu gibi değeri alır, diğer input değeri daima buna dayalı olarak yeniden hesaplanır.
React'ın güçlü bir modeli var ve componentler arasında kodu tekrar kullanmak için
inheritance
(miras) yerine composition
(birleşim) kullanılmasını öneriyoruz.
Bu bölümde, React'e yeni gelen geliştiricilerin genellikle inheritance ile ilgili karşılaştığı birkaç sorunu ele alacağız ve bunları compositionlarla nasıl çözebileceğimizi göstereceğiz.
Bazı componentler önceden çocuklarını bilmezler.
Bu, genel kutuları temsil eden Sidebar
veya Dialog
gibi componentler için özellikle geçerlidir.
Bu tür componentlerin, çocuklarını doğrudan çıktılarına yerleştirmek için children
kullanmalarını öneriyoruz:
Bildiğiniz gibi, props
sayesinde o componente tüm attributeleri alıyorduk.
props.children
dediğimizde ise o componente yerleştirilmiş child (çocuk) componentleri/içeriği (vb) alıyoruz.
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
Yukarıdaki FancyBorder
componenti bir div
return ediyor.
Bu div ise props ile color
ı parametre olarak alacak.
Alttaki WelcomeDialog
componenti ise FancyBorder
componentini return ediyor ve renk olarakta blue
gönderilmiş.
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Hoşgeldiniz
</h1>
<p className="Dialog-message">
Bu dokümantasyonu starlayarak destek olabilirsiniz!
</p>
</FancyBorder>
);
}
<FancyBorder>
JSX etiketinin içindeki herhangi bir şey children
olarak FancyBorder
componentine geçirilir.
Aşağıdaki yöntem daha az yaygındır, ancak bazen kullanılabiliyor.
Bu gibi durumlarda children
kullanmak yerine kendi yöntemlerinizi hazırlayabilirsiniz:
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
Burada atrribute olarak SplitPane
componentine left
ve right
propslarına component gönderilmiştir.
Propslar ile bir yazı, rakam, değişken, component gibi her hangi bir şeyi gönderebilirsiniz.
Bazen componentleri, diğer componentlerin "özel durumları" olarak düşünürüz.
React'te bu, daha "özel" bir componentin daha "jenerik" bir component oluşturduğu ve props ile yapılandırdığı composition ile elde edilir:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Hoşgeldiniz"
message="Bu dokümantasyonu starlayarak destek olabilirsiniz!" />
);
}
Aşağıdaki daha karışık olan örneği inceleyerek anlamaya çalışın:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Keşif Programı"
message="Size nasıl başvurmalıyız?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Kayıt Ol
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Uçağa hoşgeldiniz, ${this.state.login}!`);
}
}
Facebook binlerce componentte React kullanıyor ve component hiyerarşileri oluştururken önerdiğimiz herhangi bir kullanım durumu bulamadık.
Props and compositionlar, componentin görünümünü ve davranışını açık ve güvenli bir şekilde özelleştirmeniz için gereken tüm esnekliği verir.
Componentler arasında UI olmayan işlevselliği yeniden kullanmak istiyorsanız ayrı bir JavaScript modülü içine ayıklamanızı öneririz. Componentler içe aktarabilir ve bu fonksiyonu, nesneyi veya bir sınıfı uzatmadan kullanabilir.
Konu anlatımları sonlanmıştır. Bu aşamadan sonra Gelişmiş Kılavuzlar konularına geçiş yapılacaktır.
Temel olarak, JSX sadece React.createElement(component, props, ...children)
fonksiyonları için syntax sağlar.
JSX kodu:
<MyButton color="blue" shadowSize={2}>
Tıkla
</MyButton>
Yukarıdaki kodu, bu koda derler:
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Tıkla'
)
Hiçbir child yoksa etiketi direkt kapatarakta kullanabilirsiniz:
<div className="sidebar" />
Yukarıdaki kodu, bu koda derler:
React.createElement(
'div',
{className: 'sidebar'},
null
)
JSX'in JavaScript'e nasıl dönüştürüldüğünü test etmek isterseniz, online Babel derleyicisini deneyebilirsiniz.
Bir JSX etiketinin ilk harfi, React elementinin türünü belirler.
Büyük harfle başlayan bir JSX etiketi, bunun bir React componenti olduğunu belirtir. Bu etiketler, adlandırılmış değişkene doğrudan referans olarak derlenir; bu nedenle, JSX <Foo />
ifadesini kullanırsanız, Foo
scope (kapsam) dahilinde olmalıdır.
JSX React.createElement
çağrısına derlediğinden, React
kitaplığı daima JSX kodunuzun scope'unda olmalıdır.
Örneğin, React
ve CustomButton
doğrudan JavaScript'te atıf yapılmamasına rağmen, bu kodda her iki importta gereklidir:
import React from 'react';
import CustomButton from './CustomButton';
function WarningButton() {
// return React.createElement(CustomButton, {color: 'red'}, null);
return <CustomButton color="red" />;
}
Bir JavaScript paketleyicisi kullanmazsanız ve React'i <script>
etiketi ile yüklediyseniz, zaten React
globali scopetadır.
JSX içinde nokta işareti kullanarak bir React componentine başvurabilirsiniz. Birçok React componenti ihraç eden bir modülünüz varsa bu uygundur. Örneğin, MyComponents.DatePicker
bir component ise, bunu doğrudan JSX'den şu şekilde kullanabilirsiniz:
import React from 'react';
const MyComponents = {
DatePicker: function DatePicker(props) {
return <div>Buraya {props.color} geldiğini düşünün.</div>;
}
}
function BlueDatePicker() {
return <MyComponents.DatePicker color="blue" />;
}
Bir element türü küçük harfle başlarsa, <div>
veya <span>
gibi yerleşik bir componente atıfta bulunur ve React.createElement
alanına aktarılan bir string 'div'
veya 'span'
ile sonuçlanır. <Foo />
gibi büyük harfle başlayan türler React.createElement(Foo)
'ya derlenir ve JavaScript dosyanızda tanımlanmış veya içe aktarılmış bir componente karşılık gelir.
Componentlerin ilk harfini büyük harfle isimlendirmenizi öneririz. Küçük harfle başlayan bir componente sahipseniz, JSX'de kullanmadan önce baş harfini büyük harfe dönüştürün.
Örneğin, bu kod beklendiği gibi çalışmaz:
import React from 'react';
// Hata! Bu bir component ve baş harfi büyük yazılmış olmalıydı:
function hello(props) {
// Doğru! Div, geçerli bir HTML etiketi olduğu için <div> kullanımının meşru olduğunu söyleyebiliriz.
return <div>Merhaba {props.toWhat}</div>;
}
function HelloWorld() {
// Yanlış! React <hello />'nun büyük harf kullanmadığından bunun bir HTML etiketi olduğunu düşünür.
return <hello toWhat="World" />;
}
Bunu düzeltmek için, hello
elementini Hello
olarak yeniden adlandırıp, ona atıfta bulunmak için ise <Hello />
kullanacağız:
import React from 'react';
// Doğru! Bu bir component ve baş harfi büyük yazılmıştır:
function Hello(props) {
// Doğru! Div, geçerli bir HTML etiketi olduğu için <div> kullanımının meşru olduğunu söyleyebiliriz.
return <div>Merhaba {props.toWhat}</div>;
}
function HelloWorld() {
// Doğru! React, <Hello />'nun component olduğunu bilir çünkü büyük harfle başlamıştır.
return <Hello toWhat="World" />;
}
React element türü olarak genel bir ifade kullanamazsınız. Elementin türünü belirtmek için genel bir ifade kullanmak istiyorsanız, önce önce baş harfi büyük bir değişkene atayın. Bu, genellikle bir pakete dayalı farklı bir component oluşturmak istediğinizde ortaya çıkar:
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// Yanlış! JSX türü bir obje olamaz.
return <components[props.storyType] story={props.story} />;
}
Bunu düzeltmek için önce türü baş harfi büyük bir değişkene atayacağız:
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// Doğru! JSX türü baş harfi büyük yazılmış bir değişken olabilir.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}
Propsları JSX'de belirtmenin birkaç farklı yolu vardır.
Herhangi bir JavaScript ifadesini {}
ile çevreleyerek yazabilirsiniz. Örneğin, bu JSX'de:
<MyComponent foo={1 + 2 + 3 + 4} />
MyComponent
için, props.foo
değeri 1 + 2 + 3 + 4
ifadesini hesaplar ve 10
olur.
if
ifadelerini ve for
döngülerini JSX'te doğrudan kullanamayız. Bunun yerine, bunları çevreleyen koda yerleştirebilirsiniz.
Örneğin:
function NumberDescriber(props) {
let description;
if (props.number % 2 == 0) {
description = <strong>even</strong>;
} else {
description = <i>odd</i>;
}
return <div>{props.number}, {description} sayıdır.</div>;
}
Şartlı render ve döngüler hakkında ilgili bölümlerde daha fazla bilgi edinebilirsiniz.
Bir string ifadeyi props olarak geçirebilirsiniz. Bu iki JSX ifadesi aynıdır:
<MyComponent message="Merhaba Dünya" />
<MyComponent message={'Merhaba Dünya'} />
Bir string gönderirken, değeri HTML-unescaped'dir. Dolayısıyla bu iki JSX ifadesi aynıdır:
<MyComponent message="<3" />
<MyComponent message={'<3'} />
Değer girmezseniz, default (varsayılan) olarak propslar true
olur. Bu iki JSX ifadesi aynıdır.
<MyTextBox autocomplete />
<MyTextBox autocomplete={true} />
Genel olarak bunu kullanmanızı önermiyoruz çünkü {foo: true}
yerine, {foo: foo}
için kısa olan ES6 nesne kısayolu {foo}
ile karıştırılabilir.
Bir props
unuz varsa ve bunu JSX'de geçirmek istiyorsanız, bütün props nesnesini geçmek için ...
yı spread bir operatör olarak kullanabilirsiniz. Bu iki component aynıdır:
function App1() {
return <Greeting firstName="Ömer" lastName="Gülçiçek" />;
}
function App2() {
const props = {firstName: 'Ömer', lastName: 'Gülçiçek'};
return <Greeting {...props} />;
}
Ayrıca, tüm propsları geçirirken componentinizin tüketeceği belirli propsu ayrı olarak seçebilirsiniz.
const Button = props => {
const { kind, ...other } = props;
const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
return <button className={className} {...other} />;
};
const App = () => {
return (
<div>
<Button kind="primary" onClick={() => console.log("Tıklandı!")}>
Merhaba Dünya!
</Button>
</div>
);
};
Yukarıdaki örnekte kind
güvenli bir şekilde tüketilir ve DOM'da <button>
elemanına geçirilmez.
Diğer tüm propslar, ...other
nesnesi aracılığıyla geçirilir ve bu component gerçekten esnek hale gelir. Bir onClick
ve children
propsundan geçtiğini görebilirsiniz.
Spread attributeleri kullanışlı olabilir, ancak gereksiz componentleri kendileri için önemsemeyen componentlere veya geçersiz HTML attributelerini DOM'a geçirmeyi kolaylaştırır. Bu syntaxı dikkatli kullanmanızı öneririz.
Hem bir açılış etiketi hem de bir kapanış etiketi içeren JSX ifadelerde, bu etiketler arasındaki içerik props.children
olarak aktarılır. Childrenları geçmenin birkaç farklı yolu vardır:
Bu konu ile ilgili olan composition ve inheritance konusunuda okuyabilirsiniz.
Açılış ve kapanış etiketleri arasına bir string koyabiliriz, bu string props.children
olacaktır. Örneğin:
<MyComponent>Merhaba Dünya!</MyComponent>
Bu geçerli JSX'dir ve MyComponent
deki props.children
, basitçe "Merhaba Dünya!" stringi olacaktır.
JSX bir satırın başında ve sonunda olan boşlukları kaldırır. Boş satırları da kaldırır. Etiketlerin yanındaki yeni satırlar kaldırılır; string değişkenlerinin ortasında oluşan yeni satırlar tek bir alana yoğuşturulur. Yani aşağıdaki örneklerin hepsi aynı şeyi yapar:
<div>Merhaba Dünya/div>
<div>
Merhaba Dünya
</div>
<div>
Merhaba
Dünya
</div>
<div>
Merhaba Dünya
</div>
İstediğiniz kadar componenti children (çocuk) olarak kullanabilirsiniz, iç içe geçmiş componentleri görüntülemek için kullanışlıdır:
<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>
Bir React componenti dizi return edebilir:
render() {
// Liste öğelerini fazladan bir element ile sarmanıza gerek yok!
return [
// Keyleri unutmayın
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
Keyler hakkında detaylı bilgi için listeler ve keyler konusundan keyler başlığına bakabilirsiniz.
JavaScript ifadesini childrena {}
e koyarak geçirebilirsiniz. Örneğin, bu ifadeler aynıdır:
<MyComponent>Örnek</MyComponent>
<MyComponent>{'Örnek'}</MyComponent>
Bu, keyfi uzunlukta JSX ifadelerinin bir listesini oluşturmak için genellikle yararlıdır. Örneğin, bu bir HTML listesi oluşturur:
function Item(props) {
return <li>{props.message}</li>;
}
function TodoList() {
const todos = ['Ömer', 'Burak', 'Muhammed'];
return (
<ul>
{todos.map((message) => <Item key={message} message={message} />)}
</ul>
);
}
JavaScript ifadeleri diğer children türleri ile birlikte kullanılabilir. Bu genellikle string şablonlarının yerine yararlıdır:
function Hello(props) {
return <div>Merhaba {props.addressee}!</div>;
}
Normalde, JSX'e eklenen JavaScript ifadeleri bir stringe, bir React elementi veya bu şeylerin bir listesine göre değerlendirilir. Bununla birlikte, props.children
, yalnızca React'in nasıl yapılacağını bildiği türden değil, herhangi bir veri türünden geçebileceği için diğer herhangi bir props
gibi çalışır. Örneğin, özel bir componente sahipseniz, bir callback'i props.children
olarak almasını sağlayabilirsiniz:
// Tekrarlanan bir component üretmek için childrenların callback'leri çağırır
function Repeat(props) {
let items = [];
for (let i = 0; i < props.numTimes; i++) {
items.push(props.children(i));
}
return <div>{items}</div>;
}
function ListOfTenThings() {
return (
<Repeat numTimes={10}>
{(index) => <div key={index}>Bu listede {index} olan öğedir.</div>}
</Repeat>
);
}
false
, null
, undefined
ve true
geçerli childrenlardır. Onlar sadece render yapılmazlar. Bu JSX ifadeleri hep aynı şeyi yapacak:
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
Bu, koşullu olarak React elementlerini oluşturmak için yararlı olabilir. JSX yalnızca showHeader
true ise <Header />
componentini görür, false ise görmez:
<div>
{showHeader && <Header />}
<Content />
</div>
Bir uyarı, '0' sayısı gibi bazı falsy değerleri React tarafından render edilir. Örneğin, bu kod, props.messages
boş bir dizi olduğunda '0' yazdırılacağı için beklediğiniz gibi davranmayacaktır:
<div>
{props.messages.length &&
<MessageList messages={props.messages} />
}
</div>
Falsy değeri, boolean bağlamında değerlendirildiğinde false değerine çeviren bir değerdir. JavaScript, if-else ve döngüler gibi herhangi bir değeri bir boolena gerektiren bağlamlarda zorlamak için tür tipi dönüştürme özelliğini kullanır.
JavaScript'teki false değerlerin örnekleri (false'a çevrilir ve böylece if bloğunu atlar):
if (false)
if (null)
if (undefined)
if (0)
if (NaN)
if ('')
if ("")
if (document.all) [1]
Normalde true && kod
olduğunda ilk ifade true olduğu için sağ kısımdaki kod çalışacaktır. false && kod
olduğunda ise kod hiçbir çıktı vermeyecektir. JavaScript'te 1 && kod
olduğunda 1'i true olarak değerlendirip kodu çalıştırır fakat 0 && kod
olduğunda 0'ı false olarak değerlendirmez ve ekrana 0
yazar.
Bunu düzeltmek için, &&
önündeki ifadenin her zaman boolean olduğundan emin olun:
<div>
{props.messages.length > 0 &&
<MessageList messages={props.messages} />
}
</div>
Aksine, çıktıda false
, true
, null
veya undefined
gibi bir değerleri görmek istiyorsanız, önce bir string haline getirmeniz gerekir:
<div>
JavaScript değişkenim {String(myVariable)}.
</div>
Normalde bir React componentini düz bir JavaScript sınıfı olarak tanımlarsınız:
class Greeting extends React.Component {
render() {
return <h1>Merhaba, {this.props.name}</h1>;
}
}
ES6'yı kullanmıyorsanız, yerine create-react-class
modülünü kullanabilirsiniz:
var createReactClass = require('create-react-class');
var Greeting = createReactClass({
render: function() {
return <h1>Merhaba, {this.props.name}</h1>;
}
});
ES6 sınıflarının API'leri birkaç istisna dışında createReactClass()
a benzemektedir.
Fonksiyonlar ve ES6 sınıflarıyla defaultProps
, componentin kendisinde bir özellik olarak tanımlanır:
class Greeting extends React.Component {
// ...
}
Greeting.defaultProps = {
name: 'Ömer'
};
CreateReactClass()
ile iletilen obje üzerinde bir fonksiyon olarak getDefaultProps()
tanımlamanız gerekir:
var Greeting = createReactClass({
getDefaultProps: function() {
return {
name: 'Ömer'
};
},
// ...
});
ES6 sınıflarında, başlangıç state'ini constructor içerisinde this.state
ile tanımlayabilirsiniz.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
}
// ...
}
CreateReactClass()
ile başlangıç state'ini return eden ayrı bir getInitialState
yöntemi sağlamanız gerekir:
var Counter = createReactClass({
getInitialState: function() {
return {count: this.props.initialCount};
},
// ...
});
ES6 sınıfları ile oluşturulan componentlerde, metodlar normal ES6 sınıflarıyla aynı semantiği uygularlar. Bu, otomatik olarak this
i buton clickine bağlamadıkları anlamına gelir. Constructor içerisinde .bind(this)
i kullanmanız gerekir:
class SayHello extends React.Component {
constructor(props) {
super(props);
this.state = {message: 'Merhaba!'};
// Bu satır önemli!
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert(this.state.message);
}
render() {
// `this.handleClick` bağlı olduğundan bunu bir event işleyicisi olarak kullanabilirsiniz.
return (
<button onClick={this.handleClick}>
Selam ver
</button>
);
}
}
CreateReactClass()
ile tüm metodlar bağlandığı için buna gerek yoktur:
var SayHello = createReactClass({
getInitialState: function() {
return {message: 'Merhaba!'};
},
handleClick: function() {
alert(this.state.message);
},
render: function() {
return (
<button onClick={this.handleClick}>
Selam ver
</button>
);
}
});
Bind etmek ile ilgili detaylı bilgi için kendi yazdığım Türkçe makaleden yararlanabilirsiniz. Bknz: Bind() Fonksiyonu
ES6 sınıflarının yazılması, event işleyicileri için biraz daha fazla kalıp kod ile gelir ancak tersi büyük uygulamalarda biraz daha iyi bir performans sunar.
class SayHello extends React.Component {
constructor(props) {
super(props);
this.state = {message: 'Merhaba!'};
}
// UYARI: Bu syntax deneyseldir!
// Burada ok fonksiyonu kullanarak yöntem bağlanır:
handleClick = () => {
alert(this.state.message);
}
render() {
return (
<button onClick={this.handleClick}>
Selam ver
</button>
);
}
}
Lütfen yukarıdaki syntaxın deneysel olduğunu ve syntaxının değişebileceğini veya önerinin dilin içine girmediğini unutmayın.
Güvenli bir şekilde yazmayı tercih ederseniz birkaç seçeneğiniz vardır:
- Constructor içinde bind methodu kullanmak
- Ok fonksiyonlarını kullanmak, örneğin
onClick={(e) => this.handleClick(e)}
createReactClass
kullanmaya devam etmek
Not:
ES6 hiçbir mixin desteği olmadan başlatıldı. Bu nedenle, ES6 sınıfları ile React kullandığınızda mixin desteği olmayacaktır.
Ayrıca, mixin kullanan kodlarda çok sayıda sorun bulduk ve bunları yeni projelerinizde kullanmanızı önermiyoruz
Bu bölüm yalnızca referans içindir.
Bazen çok farklı componentler bazı ortak fonksiyonları paylaşabilir. Bunlara bazen kesişen konular da denir. createReactClass
, bunun için eski bir mixins
sistemi kullanmanızı sağlar.
Bir zaman aralığında kendisini güncellemek isteyen bir component, çok sık karşılaşılan bir kullanım durumudur. SetInterval()
kullanmak kolaydır; ancak, setInterval()
ile işiniz bittiğinde bunu iptal etmek önemlidir. React, bir component oluşturulmaya başlandığında veya yok edildiğinde sizi bilgilendiren lifecycle fonksiyonlarını kullanmanızı sağlar. Componenti yok edildiğinde otomatik olarak temizlenecek kolay bir setInterval()
methodu sağlamak için bu yöntemleri kullanan basit bir mixin oluşturalım.
Lifecycle fonksiyonları ile ilgili detaylı bilgi için State ve lifecycle konusunda "Bir Classa Lifecycle Fonksiyonları Ekleme" başlığını inceleyebilirsiniz.
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
var createReactClass = require('create-react-class');
var TickTock = createReactClass({
mixins: [SetIntervalMixin], // Mixini kullan
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000); // Mixin üzerinde bir metod çağır
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
Sayfa {this.state.seconds} saniyedir çalışıyor.
</p>
);
}
});
ReactDOM.render(
<TickTock />,
document.getElementById('example')
);
Bir component çoklu mixin kullanıyorsa ve birkaç mixin aynı lifecycle fonksiyonu tanımlarsa (diğer bir deyişle, birkaç mixin, component yok edildiğinde bazı temizlemeler yapmak isterse), tüm lifecycle fonksiyonunun çağrılmasının garanti altına alınması sağlanır.
JSX, React'ı kullanmak için şart değildir.
JSX bizlere sadece yazım kolaylıkları sağlar. JSX ile yapabileceğiniz herhangi bir şeyi düz JavaScript ile yapılabiliriz.
Örneğin, bu kod JSX ile yazılmıştır:
class Hello extends React.Component {
render() {
return <div>Merhaba {this.props.toWhat}</div>;
}
}
ReactDOM.render(
<Hello toWhat="Dünya" />,
document.getElementById('root')
);
Yukarıdaki kod, JSX kullanmayan bu koda derlenebilir:
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Merhaba ${this.props.toWhat}`);
}
}
ReactDOM.render(
React.createElement(Hello, {toWhat: 'Dünya'}, null),
document.getElementById('root')
);
JSX'in JavaScript'e dönüştürülmesiyle ilgili daha fazla örnek görmek istiyorsanız, online Babel derleyicisini deneyebilirsiniz.
Sürekli React.createElement
yazmaktan sıkıldıysak, bir kısaltma kullanmak mantıklı olacaktır:
const e = React.createElement;
ReactDOM.render(
e('div', null, 'Merhaba Dünya'),
document.getElementById('root')
);
Bu kısa formu React.createElement
için kullanırsanız, JSX olmadan React'i kullanmak elverişli olabilir.
Alternatif olarak, daha kısa bir syntax sunan react-hyperscript ve hyperscript-helpers gibi projelere başvurabilirsiniz.
Bir componentin birden çok element return etmesi React'te yaygın olarak kullanılır. Fragmentler, DOM'a fazladan etiket eklemeden childları gruplanmasına izin verir.
Normalde React'te bir componentin içeriği kapsayıcı bir element ile return edilirdi. Kapsayıcı element olmazsa hata veriyordu. Buda fazladan <div>
tagı oluşturulmasına sebep oluyordu. Fakat React v16.2.0
ile artık hayali bir kapsayıcı olan Fragment
i oluşturabiliyoruz. Bu fragmentler çıktıyı kapsadığı için içerik return edilebilecek fakat çıktıda görünmeyeceklerdir. Böylece fazladan div oluşumu önlenmiş olacaktır.
Aşağıdaki içeriği React componentinde return etmek istediğimizi düşünelim.
Biraz yazı
<h2>Başlık</h2>
Daha fazla yazı
<h2>Diğer başlık</h2>
Daha fazla yazı
React versiyon 16.2.0'dan önce bunu gerçekleştirmenin tek yolu, childları div
, span
gibi bir tag ile aşağıdaki gibi sarmalamaktır:
render() {
return (
// Fazladan bir div :(
<div>
Biraz yazı
<h2>Başlık</h2>
Daha fazla yazı
<h2>Diğer başlık</h2>
Daha fazla yazı
</div>
);
}
return edilecek değerleri <React.Fragment>
componenti içerisine yerleştirmeniz yeterli.
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Merhaba</td>
<td>Dünya</td>
</React.Fragment>
);
}
}
React.Fragment
yerine sadece Fragment
ile kapsamak isterseniz aşağıdaki gibi bir yol izleyebilirsiniz.
const Fragment = React.Fragment;
<Fragment>
<ChildA />
<ChildB />
<ChildC />
</Fragment>
// Her ikiside aynı şeydir
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
Fragmentler için yeni bir kısa syntaxta var ancak henüz tüm popüler toollar (araçlar) tarafından desteklenmemektedir.
class Columns extends React.Component {
render() {
return (
<>
<td>Merhaba</td>
<td>Dünya</td>
</>
);
}
}
<></>
kullanabilirsiniz fakat keys ya da attribute kullanımını desteklemez (Sadece kapsayıcı olarak kullanılır).
Birçok toolsun henüz kısa syntax kullanımını desteklemediğini unutmayın, bu nedenle destek gelene kadar açıkça <React.Fragment>
yazın.
<React.Fragment>
syntaxı ile bildirilen parçaların keyleri olabilir. Örneğin bir açıklama listesi oluşturmak için:
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
// `key` olmazsa, React bir key uyarısı verecektir
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</React.Fragment>
))}
</dl>
);
}
Fragment
e aktarılabilen tek attribute key
dir. Gelecekte, click-change olayları gibi ek attributeler için destek ekleyebiliriz.
React.Component
soyut temel bir sınıftır, bu nedenle doğrudan React.Componente başvurmak mantıklı değildir. Bunun yerine, genellikle classın alt classını tanımlayıp, bir render()
metodu tanımlarsınız.
Normalde bir React componentini düz bir JavaScript sınıfı olarak tanımlarsınız:
class Greeting extends React.Component {
render() {
return <h1>Merhaba, {this.props.name}</h1>;
}
}
ES6'yı henüz kullanmıyorsanız, bunun yerine create-react-class
modülünü kullanabilirsiniz. Daha fazla bilgi edinmek için ES6 olmadan React konusuna bakın.
Unutmayın, kendi temel component classlarınızı oluşturmanızı önermiyoruz. Kodun tekrar kullanımı React'te inheritancetan ziyade composition yoluyla elde edilir. Composition kullanma konusunda bir fikir edinmek için composition ve inheritance sayfasını inceleyin.
Her componentin, lifecycle fonksiyonları vardır. İçerisinde will
geçen fonksiyonlar component oluşturulmasından hemen önce çağrılırken, içerisinde did
geçen fonksiyonlar component kaldırıldıktan sonra çağrılır.
Şimdi tüm fonksiyonları bölümlendirip, ardından her birini açıklayacağız. Başlıklara çeviri yapılmamıştır, altlarına Türkçesi eklenmiştir.
Oluşturmak
Bu fonksiyonlar, bir component örneği oluşturulurken ve DOM'a eklendiğinde çağrılır:
constructor()
componentWillMount()
render()
componentDidMount()
Güncellemek
Bir güncelleme, props ya da state değişikliklerinden kaynaklanabilir. Bu fonksiyonlar, bir componentin güncellenmesiyle çağrılır.
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()
Kaldırmak
Bu fonksiyon, bir component DOM'dan kaldırıldığında çağrılır:
componentWillUnmount()
Hata işleme
Bu fonksiyon, render sırasında lifecycle fonksiyonlarında veya herhangi bir alt componentin constructoründe bir hata olduğunda çağrılır.
componentDidCatch()
Her component aşağıdaki API'leride içerisinde barındırır:
setState()
forceUpdate()
defaultProps
render()
render()
fonksiyounu zorunludur.
Çağrıldığında, this.props
ve this.state
i incelemeli ve aşağıdaki türlerden birine return etmelidir:
- React Elementleri. Genellikle JSX aracılığıyla oluşturulur. Bir element, yerel bir DOM componenti (
<div />
) veya kullanıcı tanımlı bir component (<MyComponent />
) olabilir. - String ve sayı. Bunlar DOM'da metin düğümleri olarak render edilir.
- Portaller.
ReactDOM.createPortal
ile oluşturuldu. Detaylı bilgi - null. Hiçbir şey yapmaz.
- Boolean. Hiçbir şey yapmaz. (Çoğunlukla
test
in boolean olduğu durumdareturn test && <Child />
desenini desteklemek için vardır.)
null
ya da false
return ederkem, ReactDOM.findDOMNode(this)
null
return eder.
render()
fonksiyonu, componentin state'ini değiştirmez, çağrıldığında her seferinde aynı sonucu return eder. Tarayıcıyla etkileşime girmeniz gerekiyorsa, bunun yerine componentDidMount()
ya da diğer lifecycle fonksiyonları ile çalışmalarınızı gerçekleştirin.
Not
shouldComponentUpdate()
fonksiyonufalse
return ederserender()
çağrılmayacaktır.
constructor(props)
Bir React componentinin constructorü, oluşturulmadan önce çağrılır. Bir React.Component
alt sınıfı için constructorü uygularken, herhangi bir koddan önce super(props)
u çağırmalısınız. Aksi takdirde, this.props
constructorde hatalara neden olur.
Constructore herhangi bir abonelik sunmaktan kaçının. Bunun yerine, componentDidMount()
kullanın.
Constructor, state'i başlatmak için doğru yerdir. Bunu yapmak için sadece bir nesneyi this.state
e atayın; Constructor'den setState()
fonksiyonunu çağırmaya çalışmayın. Constructor ayrıca, click-change olaylarını bind etmek için sıklıkla kullanılır.
Bind fonksiyonu hakkında detaylı bilgiye kendi yazmış olduğum Bind() fonksiyonu adlı makaleden erişebilirsiniz.
State, click-change olaylarını kullanmayacaksanız, React componenti için bir constructor oluşturmak zorunda değilsiniz.
componentWillMount()
componentWillMount()
, component oluşturulmadan hemen önce çağrılır, dolayısıyla bu yöntemde eş zamanlı olarak setState()
başlamayacaktır. Genellikle, bunun yerine constructor()
ü kullanmanızı öneririz.
componentDidMount()
componentDidMount()
, bir component render edildikten hemen sonra çağrılır. Uzak bir uç noktadan veri yüklemeniz gerekiyorsa, bu ağ isteğini başlatmak için iyi bir yerdir.
Bu fonksiyon, herhangi bir abonelik ayarlamak için iyi bir yerdir. Bunu yaparsanız, componentWillUnmount ()
da aboneliğinizi iptal etmeyi unutmayın.
Bu yöntemde setState()
çağrısı ek bir render işlemine neden olur, ancak tarayıcı ekranını güncellemeden önce gerçekleşir. render()
fonksiyonunun bu durumda iki kez çağrılmasına rağmen kullanıcının ara state'i göremez. Bu kalıp sıklıkla performans sorunlarına neden olduğundan dikkatli kullanın.
componentWillReceiveProps(nextProps)
componentWillReceiveProps()
, bir component yeni bir props almaya başlamadan önce çağrılır. Props değişikliklerine tepki olarak state güncellemeniz gerekiyorsa (örneğin sıfırlamak için), this.props
ve nextProps
metodlarını karşılaştırabilir ve bu metoddaki this.setState()
fonksiyonunu kullanarak state geçişleri gerçekleştirebilirsiniz.
React, propsta değişiklik yapılmamış olsa bile bu metodu çağırabilir, bu nedenle yalnızca değişiklikleri ele almak istiyorsanız geçerli ve sonraki değerleri karşılaştırdığınızdan emin olun.
React, mounting sırasında componentWillReceiveProps()
fonksiyonunu ilk props grubu ile çağırmaz. Componentlerin propslarının bazıları güncellenmişse bu fonksiyonu çağırır. this.setState()
fonksiyonunu çağrılması genellikle componentWillReceiveProps()
fonksiyonunu tetiklemez.
shouldComponentUpdate(nextProps, nextState)
Bir componentin çıktısı, state veya propstaki güncel değişiklikten etkilenmezse React'e bildirmek için shouldComponentUpdate()
kullanın. Varsayılan davranış, her state değişiminde tekrar render edilmesidir ve çoğu durumda varsayılan davranışı kullanmalısınız.
shouldComponentUpdate()
yeni props veya state alındığında render edilmeden önce çağrılır. Varsayılan değer true
dur. Bu fonksiyon, componentin ilk render edilişinde veya forceUpdate()
kullanıldığında çağrılmaz.
false
değerininin return edilmesi, state değiştiğinde child componentlerin yeniden render edilmesini engellemez.
shouldComponentUpdate()
fonksiyonu false
return ederse componentWillUpdate()
, render()
ve componentDidUpdate()
çağrılmayacaktır. React'in gelecekte shouldComponentUpdate()
i sıkı bir yönerge yerine bir ipucu olarak ele alabileceğini ve false
değerini return etmenin componentin yeniden render edilmesine neden olabileceğini unutmayın.
Belirli bir componentin görüntülenmesinden sonra yavaş olduğunu belirlerseniz, shouldComponentUpdate()
fonksiyonunu sığ bir props ve state karşılaştırması ile uygulayan React.PureComponent
ten devralmak için değiştirebilirsiniz. Elle yazmak istediğinizden eminseniz, this.props
ile nextProps
ve this.state
ile nextState
i karşılaştırabilir ve false
return ederek React güncellemesini geçebilirsiniz.
ShouldComponentUpdate()
te eşitlik kontrolleri yapmanızı veya JSON.stringify()
fonksiyonunu kullanmanızı önermiyoruz. Çok verimsizdir ve performansa zarar verir.
componentWillUpdate(nextProps, nextState)
componentWillUpdate()
yeni props veya state alındığında render edilmeden hemen önce çağrılır. Bunu, güncelleme gerçekleşmeden önce hazırlık yapmak için bir fırsat olarak kullanın. Bu fonksiyon ilk render için çağrılmaz.
Burada this.setState()
fonksiyonunu çağıramayacağınızı unutmayın; componentWillUpdate()
return etmeden önce bir React componentinin güncellemesini tetikleyecek başka bir şey yapmanız (örn. bir Redux eylemi göndermeniz) gerekir.
Props değişikliklerine tepki olarak state'i güncellemek istiyorsanız, bunun yerine componentWillReceiveProps()
kullanın.
Not
shouldComponentUpdate()
false return ederse,componentWillUpdate()
çağrılmayacaktır.
componentDidUpdate(prevProps, prevState)
componentDidUpdate()
, güncelleme gerçekleştikten hemen sonra çağrılır. Bu fonksiyon ilk render için çağrılmaz.
Bunu, component güncellendiğinde DOM üzerinde çalışmak için kullanın. Bu ayrıca, mevcut yeri önceki yerlerle kıyasladığınız sürece network istekleri yapmak için iyi bir yerdir (örneğin props değişmediğinde bir network isteği gerekmeyebilir).
Not
shouldComponentUpdate()
false return ederse,componentDidUpdate()
çağrılmayacaktır.
componentWillUnmount()
componentWillUnmount()
, bir component unmounted ve destroyed edilmeden hemen önce çağrılır. Bu fonksiyonda, zamanlayıcıları geçersiz kılma, network isteklerini iptal etme veya componentDidMount()
fonksiyonunda oluşturulan abonelikleri temizleme gibi gerekli temizliği yapın.
componentDidCatch(error, info)
Hata sınırları, alt component ağacının herhangi bir yerindeki JavaScript hatalarını yakalayan, bu hataları log'a yazan ve çöktüğü compoennt ağacı yerine bir yedek UI görüntüleyen React componentleridir. Hata sınırları, render, lifecycle fonksiyonlar ve altındaki ağacın constructorlerinde hataları yakalar.
Beklenmedik istisnalardan kurtarmak için yalnızca hata sınırlarını kullanın; onları kontrol akışı için kullanmaya çalışmayın.
Daha fazla ayrıntı için React 16'daki Error Handling bölümüne bakın.
Not
Hata sınırları yalnızca alt componentlerin içindeki hataları yakalar . Bir hata sınırı kendi içinde bir hata yakalayamaz.
setState(updater[, callback])
setState()
, component state'indeki değişiklikleri ekler ve bu componentin çocuklarının güncellenen state ile yeniden render edilmesini gerektiğini React'e bildirir. Bu, kullanıcı arabirimini güncellemek için kullandığınız birincil yöntemdir.
Componenti güncellemek için hemen bir komut yerine setState()
fonksiyonunu çağırın. Daha iyi bir performans için React onu geciktirebilir ve sonra birkaç componenti tek seferde güncelleştirebilir.
setState()
her zaman componenti hemen güncellemez. Güncelleme işini daha sonraya bırakıp toplu olarak yapabilir. Bu, setState()
fonksiyonunu potansiyel bir tuzağa düşürdükten sonra this.state
dosyasını okumayı kolaylaştırır. Bunun yerine, componentDidUpdate
veya setState(updater, callback)
kullanın; ikisi de güncelleme uygulandıktan sonra tetiklenecektir. State'i bir önceki state'e göre ayarlamanız gerekiyorsa, aşağıdaki updater
argümanını okuyun.
(prevState, props) => stateChange
prevState
, önceki state'e yapılan atıftır. Doğrudan değişime uğratılmamalıdır. Bunun yerine, değişiklikler prevState
ve props
parametrelerine dayanan yeni bir nesne oluşturarak temsil edilmelidir. Örneğin, stateteki bir değeri props.step
ile artırmak istediğimizi varsayalım:
this.setState((prevState, props) => {
return {counter: prevState.counter + props.step};
});
Güncelleyici fonksiyonu tarafından alınan hem prevState
hem de props
un güncel olması garanti edilir. Güncelleyicinin çıktısı derhal prevState
ile birleştirilir.
SetState()
fonksiyonunun ikinci parametresi, setState
tamamlandıktan ve component yeniden render edildikten sonra yürütecek isteğe bağlı bir callback fonksiyonudur. Genellikle bunun yerine bu mantık için componentDidUpdate()
kullanmanızı öneririz.
İsteğe bağlı olarak, bir objeyi bir fonksiyon yerine setState()
in ilk argümanı olarak geçirebilirsiniz:
setState(stateChange[, callback])
Bu, yeni bir state'e, örneğin bir alışveriş sepeti öğe miktarını ayarlamak için stateChange
öğesinin sığ bir birleştirme gerçekleştirir:
this.setState({quantity: 2})
Bu setState()
şekli aynı zamanda eşzamansızdır ve aynı döngüde birlikte kullanılabilir. Örneğin, aynı döngüde bir maddenin miktarını birden çok kez artırmayı denerseniz, eşdeğerlik şu şekilde sonuçlanacaktır:
Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)
Sonraki çağrılar aynı döngüdeki önceki çağrıların değerlerini geçersiz kılacak, bu nedenle miktar yalnızca bir kez artırılacaktır. Bir sonraki state önceki state'e bağlıysa, bunun yerine updater işlev formunu kullanmanızı öneririz:
this.setState((prevState) => {
return {quantity: prevState.quantity + 1};
});
Detaylı bilgi için state ve lifecycle sayfasını inceleyenilirsiniz.
component.forceUpdate(callback)
Varsayılan olarak, componentinde state ya da props değiştiği zaman, component yeniden render edilir. render()
fonksiyonu diğer bazı verilere bağlı ise, forceUpdate()
fonksiyonunu çağırarak componentin yeniden oluşturulması gerektiğini React'e bildirebilirsiniz.
forceUpdate()
çağrısı, shouldComponentUpdate()
atlanarak componentte render()
çağırılmasına neden olur. Bu, her bir çocuğun shouldComponentUpdate()
fonksiyonu da dahil olmak üzere, alt componentlerin lifecycle fonksiyonlarını tetikleyecektir.
forceUpdate()
fonksiyonunun tüm kullanımlarından kaçınmaya çalışmalısınız ve sadece render()
da this.props
ve this.state
ten okuma yapmalısınız.
defaultProps
, class için varsayılan props koymak için component clasının kendisinde bir özellik olarak tanımlanabilir. Bu, tanımlanmamış props için kullanılır, ancak boş props için kullanılamaz. Örneğin:
class CustomButton extends React.Component {
// ...
}
CustomButton.defaultProps = {
color: 'blue'
};
props.color
parametre olarak gönderilmezse, varsayılan olarak blue
değerini alır:
render() {
return <CustomButton />; // props.color, blue olacaktır
}
props.color
null tanımlanırsa, null olacaktır:
render() {
return <CustomButton color={null} /> ; // props.color, null olacaktır
}
Single page application yani kısa adıyla SPA, tek HTML sayfası yükleyen bir uygulamadır ve uygulamanın çalışması için gerekli tüm dosyaları (JavaScript, CSS vb) içerir. Sayfa veya sonraki sayfalarla olan herhangi bir etkileşim için servera gidip gelmesi gerektirmez; bu da sayfanın yeniden yüklenmediği anlamına gelir.
Reactte SPA oluşturabilmenize rağmen, bu bir zorunluluk değildir. React, hali hazırda çalışan bir sitenin küçük bölümlerini geliştirmek için de kullanılabilir. React'te yazılmış kod, diğer diller ile de kullanılabilir. Facebook'un sitesi buna en iyi örnektir.
Bu kısaltmalar, JavaScript dilinin bir uygulaması olan ECMAScript standartlarının en yeni sürümlerine karşılık gelir. ES6 sürümü (ES2015 olarak da bilinir), ok fonksiyonları, classlar, şablon değişmezleri, let
ve const
ifadeleri gibi önceki sürümlere pek çok ekleme içerir.
Bir JavaScript derleyicisi, güncel sürümlerde yazılmış (örneğin ES6) kodunu alır, tarayıcıların anlayacağı syntaxa dönüştürür. Bunun için React ile en sık kullanılan derleyici Babeldir.
Bundlerlar (paketleyiciler), JavaScript ve CSS kodunu ayrı modüller (çoğunlukla yüzlerce tanesi) olarak yazarlar ve tarayıcılar için daha iyi optimize edilmiş birkaç dosyaya birleştirirler. React uygulamalarında yaygın olarak kullanılan bazı paketleyiciler arasında Webpack ve Browserify bulunur.
Package manager (Paket yöneticileri), projenizdeki bağımlılıkları yönetmenize izin veren araçlardır. npm ve yarn, React uygulamalarda yaygın olarak kullanılan iki paket yöneticisidir. Her ikisi de aynı npm paketi kayıt defteri için istemcilerdir.
CDN, Content Delivery Network (İçerik Dağıtım Ağı) kısaltmasıdır. CDN'ler, tüm dünyadaki bir sunucudan önbelleklenmiş statik içeriği sağlar.
JSX, JavaScript'in syntax uzantısıdır. Bir şablon diline benzer ancak JavaScript özellikleri vardır. JSX, JavaScript nesnelerini return eden React.createElement()
çağrılarına derlenir. JSX'e temel bir giriş elde etmek için dokümanlara bakın ve burada JSX hakkında daha ayrıntılı bir bilgi bulabilirsiniz.
React DOM, HTML attribute adları yerine camelCase adlandırma kuralını kullanıyor. Örneğin, JSX'de tabindex
, tabIndex
olur. Class
özelliği de JavaScript'de ayrılmış bir sözcük olduğu için class
niteliği className
olarak da yazılmıştır:
const name = 'Ömer Gülçiçek';
ReactDOM.render(
<h1 className="hello">Benim adım {name}!</h1>,
document.getElementById('root')
);
React elementleri, React uygulamalarının yapı taşlarıdır. Daha yaygın olarak bilinen component kavramıyla elementler karıştırabilir. Bir element, ekranda görmek istediğiniz şeyi tanımlar. React elementleri değişmez.
const element = <h1>Merhaba Dünya</h1>;
Elementler doğrudan kullanılmaz, ancak componentler ile return edilir.
Elementler hakkında detaylı bilgi için elementleri render etmek konusunu inceleyebilirsiniz.
React componentleri, sayfaya uygulanacak bir React elementini return eden, küçük, tekrar kullanılabilir kod parçacıklarıdır. React componentinin en basit sürümü, React elementi return eden basit bir JavaScript fonksiyonuna örnek olarak:
function Welcome(props) {
return <h1>Merhaba {props.name}</h1>;
}
Componentler ES6 classları ile de yapılabilir:
class Welcome extends React.Component {
render() {
return <h1>Merhaba {this.props.name}</h1>;
}
}
Componentler, farklı parçalara bölünebilir ve başka component içinde kullanılabilir. Componentler, diğer componentleri, dizileri, stringleri ve sayıları return edebilir. UI'ızın bir bölümünü birkaç kez kullandıysa (Button, Panel, Avatar) veya kendi başına yeterince karmaşıksa (App, FeedStory, Comment), yeniden kullanılabilir bir component olması küçük parçalara ayırmak iyi bir yoldur. Component adları daima büyük harfle başlamalıdır ( değil, olmalı).
Component oluşturma hakkında daha fazla bilgi için component dokümanına bakın.
props
bir React componentinde, bir üst componentten alt componentlere geçirilen verilerdir.
Unutmayın ki props
lar yalnızca okunurdur; hiçbir şekilde değiştirilmemelidirler:
// Yanlış!
props.number = 42;
Her component için props.children
mevcuttur. Bir componentin açılış ve kapanış etiketleri arasındaki içeriğe denir. Örneğin:
<Welcome>Merhaba Dünya</Welcome>
Merhaba Dünya
stringi Welcome
componentinde props.children
da tutulur:
function Welcome(props) {
return <p>{props.children}</p>;
}
Class componentte ise this.props.children
ile kullanılır:
class Welcome extends React.Component {
render() {
return <p>{this.props.children}</p>;
}
}
Bir Component, kendisiyle ilişkili bazı veriler zaman içinde değiştiğinde state
e ihtiyaç duyar.
state
ile props
arasındaki en önemli fark, props
un ana componentten geçirilmiş olmasıdır, ancak state
componentin kendisi tarafından yönetilmektedir. Bir component propslarını değiştiremez, ancak statei değiştirebilir. Bunu yapmak için, this.setState()
i çağırmalıdır. Yalnızca class olarak tanımlanan componentlerin state'i olabilir.
Lifecycle fonksiyonları, bir componentin farklı aşamalarında yürütülen özel fonksiyonlardır. Component oluşturulduğunda ve DOM'a eklendiğinde (mounting), component güncellendiğinde ve component kaldırıldığında DOM'da çalıştırılan fonksiyonlar vardır.
Lifecycle fonksiyonları hakkında daha fazla bilgi için lifecycle fonksiyonları dokümanına bakın.
Bir key
elementin dizileri oluşturulurken eklenmesi gereken özel bir string attributetüdür. Keyler yardımı ile hangi elementlerin değiştiğini, eklendiğini veya kaldırıldığını belirleyebilirsiniz. Elementlere istikrarlı bir kimlik kazandırmak için bir dizideki elemente key verilmelidir.
Keyler yalnızca aynı dizindeki kardeş elementler arasında benzersiz olmalıdır. Tüm uygulamada benzersiz olması gerekmez.
Keylere Math.random()
gibi bir şey kullanmayın. React keylerinin istikrarlı bir kimliği olması, böylece React'in element ekleme, kaldırma gibi işlemleri belirleyebilmesi için önemlidir. İdeal olarak, keyler verilerinizden gelen benzersiz ve kararlı tanımlayıcılara (örneğin post.id
) karşılık gelmelidir.
Keyler hakkında daha fazla bilgi için listeler ve keyler dokümanına bakın.
React ile istediğiniz herhangi bir AJAX kütüphanesini kullanabilirsiniz.
Popüler olanlar Axios, jQuery AJAX ve tarayıcıda yerleşik olarak bulunan window.fetch.
AJAX isteklerini componentDidMount
fonksiyonunda kullanmalısınız. AJAX isteğinden gelen veriyi setState
yardımıyla state'e atarak componentin içerisinde kullanabilirsiniz.
Aşağıdaki component, state
i doldurmak için componentDidMount
ta bir AJAX çağrısının nasıl yapılacağını gösterir:
{
items: [
{ id: 1, name: 'Apples', price: '$2' },
{ id: 2, name: 'Peaches', price: '$5' }
]
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
//AJAX isteğini burada başlatıyoruz.
fetch("https://api.example.com/items")
.then(res => res.json())
.then(
(result) => {
//AJAX'tan gelen veri ile state'imizi güncelliyoruz.
this.setState({
isLoaded: true,
items: result.items
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Yükleniyor...</div>;
} else {
return (
<ul>
{items.map(item => (
<li key={item.name}>
{item.name} {item.price}
</li>
))}
</ul>
);
}
}
}
Gelişmiş kılavuzlar sonlanmıştır. Bu aşamadan sonra Uygulamalı Eğitime geçiş yapılacaktır.
Uygulamalı olarak adım adım XOX oyununu geliştireceğiz, projenin bitmiş hali şudur: XOX Oyunu
CSS kodları hazır olarak verilmiş durumda, biz sadece JavaScript geliştirmesi yapacağız.
XOX oyunu için görseldeki gibi 3x3'lük bir oyun alanına ihtiyacımız vardır.
Burada her bir kareyi oluşturan componentimizin adı Square
olacak.
9 kareyi birleştirerek oyun alanını render eden componentimizin adı ise Board
olacak.
Game
componenti ise tüm içeriği kapsayan bir tablo render edecek.
Yani başlıca üç componente sahibiz:
- Square
- Board
- Game
Başlangıç için XOX Başlangıç Kodunu kullanacağız.
Kod uzun ve karmaşık gelebilir, fakat incelediğinizde basit şeyler ile oluşturulduğunu ve aslında karmaşık olmadığını anlayacaksınız. Yeterli düzeyde olduğunuzu düşünmüyorsanız react dokümanının en başına dönerek bilgilerinizi tazeleyebilirsiniz.
Board
componentinden Square
componentine bazı verileri geçirmeyi deneyelim.
Board'un renderSquare
fonksiyonunda, bir value
propsu Square
ye geçirmek için kodu değiştirin:
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />;
}
Daha sonra {/ * TODO * /}
yazan kısmı {this.props.value}
ile Square'ın render
fonksiyonunu değiştirin:
class Square extends React.Component {
render() {
return (
<button className="square">
{this.props.value}
</button>
);
}
}
Önce: Kodun ilk halinde karelerin içi boştu.
Sonra: Render edilmiş çıktıda her karede bir sayı görmeniz gerekiyor.
Board
componentinin render fonksiyonundan this.renderSquare()
fonksiyonu çağırılıyor. Parametre olarak ise 0'dan 8'e kadar parametreler gönderilmiş. this.renderSquare
fonksiyonu ise Square
componentini return ediyordu. Fakat içerisinde herhangi bir içerik yoktu. value={i}
yaparak Square
componentine value
değerleri gönderdik. Square
componentinde ise gelen bu valueyu this.props.value
ile yazdırdık. Böylece oyun alanımızda karelerde 0'dan 8'e kadar rakamlar yazdırarak verileri props üzerinden componente geçirmiş olduk.
Square
componentine tıkladığınızda bir "X" işareti dolduracak şekilde güncelleyelim. Square
componentinde render()
fonksiyonunu şöyle değiştirmeyi deneyin:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => alert('tıklandı')}>
{this.props.value}
</button>
);
}
}
Bir kareye tıklarsanız, tarayıcınızda alert (uyarı) almanız gerekiyor.
Burada JavaScript ok fonksiyon syntaxı kullanılmıştır. Fonksiyonu onClick
propsu olarak geçtiğimizi unutmayın.
Normalde click change gibi olaylarda bind etmek gerekiyor fakat ok fonksiyonu ve bazı diğer yöntemleri kullanarak constructorde bind etmedende click ve change'i kullanabiliyoruz. Detaylar için click ve change olayları başlığına bakabilirsiniz.
OnClick = {alert ('tıklandı')}
olarak yazsaydık, click yapmak yerine sadece uyarırdı.
React componentleri, constructorde this.state
e sahip olabilirler; bu component için özel olarak düşünülmelidir. Kareye tıklatıldığında değişmesini sağlayalım.
İlk önce, state'i başlatmak için classa bir constructor ekleyin:
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
render() {
return (
<button className="square" onClick={() => alert('tıklandı')}>
{this.props.value}
</button>
);
}
}
JavaScript classlarında, bir alt clasın constructorünü tanımlarken, super();
i çağırmanız gerekir.
Şimdi ise click yapıldığında karede X yazması için state'i güncelleyecek şekilde kodumuzu değiştirelim.
this.props.value
uthis.state.value
ile<button>
etiketinin içinde değiştirin.
Önceki halinde üst componentten parametre alıyorduk fakat artık state'teki değeri alacağımız için props
yerine state
kullanmalıyız.
() => alert()
ı() => this.setState({value: 'X'})
olacak şekilde değiştirin.
Daha sonra X ve O yazması için gerekli kodları ekleyeceğiz. Şimdilik her click yapıldığında stateteki valueyu X olacak şekilde güncelliyoruz. State güncellendiğinde component tekrardan render edildiği için güncel değeri karenin içerisine yazacaktır.
Şimdi <button>
tagımız şu şekilde görünmelidir:
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>
);
}
}
this.setState
çağrıldığında, component için bir güncelleme planlanır ve componenti alt öğeleri ile birlikte yeniden render edilmesine neden olur. Component yeniden render edildiğinde, this.state.value
X
olur, böylece karede bir X görürsünüz.
Herhangi bir kareyi tıklarsanız, içinde bir X görünmelidir.
Önceki kod örneğinde, .slice()
fonksiyonunu kullanarak squares
dizisinde değişiklik yapmadan önce kopyalamayı ve varolan dizinin orijinal halini korumanızı öneririz. Bunun ne anlama geldiğini ve neden önemli bir konu olduğunu konuşalım.
Verileri değiştirmek için genellikle iki yol vardır. İlk yöntem, bir değişkenin değerlerini doğrudan değiştirmek. İkinci yöntem ise veriyi istenen değişiklikleri de içeren yeni bir kopyasını oluşturmaktır.
var player = {score: 1, name: 'Ömer'};
player.score = 2;
// player objesinin son hali: {score: 2, name: 'Ömer'}
var player = {score: 1, name: 'Ömer'};
var newPlayer = Object.assign({}, player, {score: 2});
// player objesi değişmedi ve kopyası oluşuturup score değerinde güncelleme yapıldı.
// newPlayer objesinin son hali: {score: 2, name: 'Ömer'}
// Veya obje yayılım syntax kullanıyorsanız, şu şekilde de yazabilirsiniz:
// var newPlayer = {...player, score: 2};
Değişmezlik ayrıca bazı karmaşık özelliklerin uygulanmasını çok daha kolay hale getirir. Örneğin, bu eğitimde oyunun farklı aşamaları arasında zaman yolculuğu yapacağız.
XOX oynarken önceki adımlara gidebilmek için adım adım aşamaları kayıt altına almak gerekiyor. Daha sonra uygulamamızın sağ tarafına, önceki adımlara gidebilmek için butonlar ekleyeceğiz.
Değişmezlik ayrıca bazı karmaşık özelliklerin uygulanmasını çok daha kolay hale getirir. Örneğin, bu eğitimde oyunun farklı aşamaları arasında zaman yolculuğu yapacağız.
Değişikliğe uğrayan bir objenin değişip değişmediğini belirlemek zordur, çünkü değişiklikler doğrudan objeye yapılır. Bu daha sonra, geçerli objeyi önceki bir kopyayla karşılaştırma, tüm obje ağacını çaprazlama ve her değişkeni ve değeri karşılaştırmayı gerektirir. Bu süreç giderek daha karmaşık hale gelebilir.
Değiştirilebilir bir objenin nasıl değiştiğini belirlemek oldukça kolay. Objenin son hali daha öncekinden farklıysa obje değişmiş demektir, bu kadar.
React'te değiştirilemezliğin en büyük yararı, saf componentleri oluşturduğunuzda gelir. Değişmez veriler, değişikliklerin yapılıp yapılamadığını daha kolay belirleyebildiğinden, bir componentin ne zaman yeniden oluşturulmasını istediğini belirlemeye yardımcı olur.
Constructorü kaldırdık ve aslında React, fonksiyonel component olarak adlandırılan yalnızca bir render
fonksiyonundan oluşan Square gibi component türleri için daha basit bir syntax destekliyor. extends React.Component
gibi uzunca class tanımlamak yerine props alan ve render edilecek olanı return eden bir fonksiyon yazın.
Tüm Square classını bu fonksiyonla değiştirin:
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
Uygulamalarınızdaki birçok component fonksiyonel component olarak yazılabilir. Bu componentleri yazmak daha kolaydır ve React tarafından optimize edilirler.
onClick={() => props.onClick()}
i de yalnızca onClick={props.onClick()}
olarak değiştirdik. onClick={props.onClick()}
işe yaramayacağına dikkat edin, çünkü onu aşağıya aktarmak yerine props.onClick
i çağırırdı.
Oyunumuzda bariz bir hata bulunmakta, sadece X hamle yapabiliyor; bunu düzeltelim.
Varsayılan ilk hareketin 'X' olacağına karar verelim. Başlangıç state'ini Board
constructoründe değiştirelim:
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
xIsNext: true,
};
}
Her hamle yapıldığında xIsNext
değerini değiştirerek sıranın X
ya da O
ya geçmesini sağlayalım. Board'un handleClick
fonksiyonunu xIsNext
değerini değiştirmek için güncelleyin:
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = this.state.xIsNext ? 'X' : 'O';
this.setState({
squares: squares,
xIsNext: !this.state.xIsNext,
});
}
Şimdi X ve O sırayla hamle yapacak. Ardından, Board'un render
fonksiyonu içindeki status
stringini de değiştirip, sıradaki oyuncunun kim olduğunu gösterelim:
render() {
const status = 'Sıradaki Oyuncu: ' + (this.state.xIsNext ? 'X' : 'O');
return (
// geri kalan kodlar değişmedi
Bu değişikliklerden sonra Board componentinin son hali şu şekilde olmalı:
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 = 'Sıradaki Oyuncu: ' + (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>
);
}
}
Bir oyunun ne zaman kazanılacağını gösterelim. Bu fonksiyonu dosyanın sonuna ekleyin:
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;
}
Birisi oyunu kazanırsa, kimin kazanıp kazanmadığını kontrol etmek ve state metnini "Kazanan: [X/O]" gösterilmesini sağlamak için Board
componentinin render
fonksiyonuna gerekli kodları ekleyelim.
Board'un render
ındaki status
bildirimini şu kodla değiştirin:
render() {
const winner = calculateWinner(this.state.squares);
let status;
if (winner) {
status = 'Kazanan: ' + winner;
} else {
status = 'Sıradaki Oyuncu: ' + (this.state.xIsNext ? 'X' : 'O');
}
return (
// geri kalanı değişmedi
Artık, oyunda birisi kazanmışsa veya bir kare zaten doldurulmuşsa tıklamayı erkenden return etmek için Board
un handleClick
ini değiştirebilirsin:
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,
});
}
Tebrik ederiz! Artık tic-tac-toe oyunu geliştirdiniz. Ve şimdi React'in temellerini uygulamalı olarak gördünüz. Burada asıl kazanan sizsiniz !