Skip to content

Latest commit

 

History

History
4457 lines (3282 loc) · 144 KB

reactjs.md

File metadata and controls

4457 lines (3282 loc) · 144 KB

Ömer Gülçiçek

React Js Dokümanı

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.


React JS Kurulumu

Online olarak katılmayı deneyin ya da local geliştirme ortamınızı oluşturun.

Online Kod Ortamı

React'i online kod ortamında denemek istiyorsanız, CodePen ya da CodeSandbox sitelerini kullanabilirsiniz.

React HTML Şablonu

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.

Hızlı Başlangıç

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.

Geliştirme Ortamı

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 Kurulumu

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

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.

Projeyi Build Etmek

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.


Merhaba Dünya

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.

JavaScript ile ilgili bir not

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


JSX Nedir?

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.

Neden JSX?

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!

Javascript ifadelerini JSX'e Yerleştirmek

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

CodePen'de Deneyin

Fonksiyonları JSX'e Yerleştirmek

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

JSX ile Attribute Belirlemek

String ifadeleri attribute olarak belirtmek için tırnak işaretleri kullanabilirsiniz:
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 yerine className, tabindex yerine tabIndex kullanılır.

JSX ile Child Belirlemek

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.

JSX, Enjeksiyon Saldırılarını Önler

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 &amp;, < ifadesi &lt;, > ifadesi &gt;, " ifadesi &quot;, ' ifadesi &#39; ş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.

JSX Nesneleri Temsil Eder

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.


Elementleri Render Etmek

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.

Bir Elementi DOM'a Render Etmek

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

CodePen'de Deneyin

Sayfanızda "Merhaba Dünya!" başlığı görüntülenecektir.

Oluşturulan Bir Elementi Güncellemek

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

CodePen'de Deneyin

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 stateler 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.

react dom örneği


Component ve Props

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.

Fonksiyonel ve Class Componentleri

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.

Componentleri Render Etmek

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

CodePen'de Deneyin

Bu örnekteki olayları aşama aşama inceleyelim:

  1. ReactDOM.render() fonksiyonundan <Welcome name='Ömer' /> elementini çağırırız.

  2. React, Welcome componentine parametre olarak {name: 'Ömer'} objesini alır.

  3. Welcome componenti <h1>Merhaba Ömer</h1> elementini return eder.

  4. 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.

Componentleri Oluşturmak

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

CodePen'de Deneyin

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:

  1. ReactDOM.render() ilk olarak <App /> componentini çağırdı.

  2. <App /> componenti ise 3 tane <Welcome /> componenti return edecek. Fakat <Welcome /> componentine parametre olarak farklı objeler yollanmış.

  3. İlk <Welcome /> componenti ekrana Merhaba Ömer yazacak. Ardından tekrar <Welcome /> componenti çağrılacak fakat bu sefer objedeki ad Muhammed, son olarakta Burak olacak.

  4. Sonuç olarak ekranda bir div içerisinde Merhaba Ömer, Merhaba Muhammed ve Merhaba Burak yazan başlıklar yazdırılacaktır.

Componentleri Parçalamak

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

CodePen'de Deneyin

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

CodePen'de Deneyin

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.

Propslar Yalnızca Okunabilir

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.


State ve Lifecycle

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

CodePen'de Deneyin

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

CodePen'de Deneyin

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ı propstur. 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.

Bir Fonksiyonun Class'a Dönüştürülmesi

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.

  1. React.Componentine extend eden aynı ada sahip bir ES6 classı oluşturalım. (class Clock extends React.Component)

  2. Buna render() adı verilen boş bir fonksiyon ekleyin.

  3. Fonksiyonun içerisindeki kodları render()ın içerisine taşıyın.

  4. propsun 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>
    );
  }
}

CodePen'de Deneyin

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

Bir class'a state eklemek için aşağıdaki 3 adımı dikkatlice inceleyiniz.

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

propsun constructorden nasıl alındığına dikkat ediniz:

constructore 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')
);

CodePen'de Deneyin

Şimdi, Clocki kendi zamanlayıcısını oluşturup her saniyede bir güncelleyeceğiz.

Bir Classa Lifecycle Fonksiyonları Ekleme

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ı thisin ü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 propsları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 (propsları) 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')
);

CodePen'de Deneyin

Neler olup bittiğini hızla özetleyelim:

  1. ReactDOM.render() içerisinde <Clock /> componenti çağırıldığında, React Clock componentinin constructorünü çağırır. Clock componentinin geçerli saati göstermesi gerektiği için this.state'i geçerli saat ile başlatır. Daha sonra bu state güncellenecek.

  2. React sonra Clock componentinin render() 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.

  3. Clock componentinin çıktısı DOM'a eklendiğinde, React, componentDidMount() fonksiyonunu çağırır. Her saniye çalışacak olan tick() fonksiyonunu timerIDde tutar.

  4. Her saniye tarayıcı tick() fonksiyonunu çağırır. Clock componentinin içinde, geçerli saati içeren bir nesneyle setState()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 tekrar render() eder. Bu sefer render() yöntemindeki this.state.date güncellenmiş olacaktır. React DOM'u buna göre günceller.

  5. Clock componenti DOM'dan kaldırıldıysa, React, timerID durduğu anda componentWillUnmount() fonksiyonunu çağırır.

State'i Doğru Kullanmak

setState() hakkında bilmeniz gereken üç şey vardır.

State'i Doğrudan Değiştirmeyin

Ö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.statei atayabileceğiniz tek yer constructordür.

State Güncellemeleri Asenkron Olabilir

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
  };
});
State'leri Toplu Güncelleştirmek

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

setStatei 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.


Click ve Change Olayları

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 addEventListeneri ç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')
);

CodePen'de Deneyin

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 onClicke 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, LoggingButtonun 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.

Bağımsız Değişkenleri Event Olaylarına Aktarma

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.


Şartlı Render

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

CodePen'de Deneyin

Adım adım incelemekte fayda var.

  1. İlk olarak ReactDOM, Greeting componentini render ediyor.

  2. Farkettiğiniz gibi componentin içerisinden props olacak bir isLoggedIn={false} değerini gönderiyor.

  3. Greeting componentinde, parametre olarak gönderilen boolean (true ya da false) değeri bir değişkene atıyor.

  4. Ardından bu değer true ise UserGreeting componentini, false ise GuestGreeting componentini return ediyor.

Element Değişkenleri

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

CodePen'de Deneyin

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.

&& Operatö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')
);

CodePen'de Deneyin

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.

If-Else Şartlı Operatörü

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.

Componentin Render Edilmesini Önlemek

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

CodePen'de Deneyin

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.


Listeler ve Keyler

Ö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.

Birden Çok Component Vermek

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 listItemse 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')
);

CodePen'de Deneyin

Bu kod, ekrana ul kapsayıcısı ve liler içerisinde rakamları yazacaktır.

Temel Liste Componenti

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

CodePen'de Deneyin

Keyler

Keyler 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 liler) 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 keylere 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.

Keyler Yalnızca Kardeşleri Arasında Benzersiz Olmalıdır

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

CodePen'de Deneyin

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.idyi okuyabilir, ancak props.keyi okuyamaz.

map() Fonksiyonunu JSX İçine Karıştırmak

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

CodePen'de Deneyin

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.


Formlar

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>

Kontrollü Componentler

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

CodePen'de Deneyin

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.

Textarea Tagı

HTML'de, <textarea> tagı yazıyı çocuğunda tanımlar:

Yani yazılan yazı inputta 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.valuein constructor'te başlatıldığına dikkat edin, böylece textarea içerisinde varsayılan olarak bu yazı bulunacaktır.

Select Tagı

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

CodePen'de Deneyin

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 diziyi value attribute'üne yazabilirsiniz:

<select multiple={true} value={['B', 'C']}>

File Input Tagı

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, propslar gibi.)

Çoklu Girişleri Ele Alma

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

CodePen'de Deneyin

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ü Giriş Boş Değer

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

State Güncellemek

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

CodePen'de Deneyin

İkinci Inputu Eklemek

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

CodePen'de Deneyin

Ş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.

Dönüştürme Fonksiyonları

İ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.

State Güncellemek

Ş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 TemperatureInputden çı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 veya onTemperatureChange adlarının özel bir anlamı yoktur. Onlara, value ve onChange 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>
    );
  }
}

CodePen'de Deneyin

Ş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.

Monitoring State in React DevTools


Composition ve Inheritance

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.

Containment (Kapsama)

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

CodePen'de Deneyin

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

CodePen'de Deneyin

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.

Uzmanca Kod

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!" />
  );
}

CodePen'de Deneyin

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

CodePen'de Deneyin

Inheritance Hakkında

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.


Derinlemesine JSX

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.

React'te Element Tipini Belirleme

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.

React Scope'ta Olmalı

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.

Nokta Notasyonunu JSX Türü İçin Kullanma

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

Componentler Büyük Harf İle Başlamalıdır

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

Runtime Olarak Tür Seçimi

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

JSX'te Propslar

Propsları JSX'de belirtmenin birkaç farklı yolu vardır.

JavaScript İfadeleri Olarak Props

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.

String Literaller

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="&lt;3" />

<MyComponent message={'<3'} />

Propslar Default Olarak True'dur

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.

Spread Attributeler

Bir propsunuz 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.

JSX'te Children

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.

String Literaller

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 MyComponentdeki 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>

JSX'te Children

İ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.

Children JavaScript İfadeleri

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

Children Türünden Functions

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

Booleans, Null ve Undefined Yok Sayılır

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>

ES6 Olmadan React

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.

Varsayılan Propsları Bildirmek

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

  // ...

});

Başlangıç State'ini Ayarlama

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

Autobinding

ES6 sınıfları ile oluşturulan componentlerde, metodlar normal ES6 sınıflarıyla aynı semantiği uygularlar. Bu, otomatik olarak thisi 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

Mixinler

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 Olmadan React

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.


Fragmentler

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

Kullanımı

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>

Kısa Syntax

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.

Keyli Fragmentler

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

Fragmente aktarılabilen tek attribute keydir. Gelecekte, click-change olayları gibi ek attributeler için destek ekleyebiliriz.

CodePen'de Deneyin


Lifecycle Fonksiyonları

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.

Mounting

Oluşturmak

Bu fonksiyonlar, bir component örneği oluşturulurken ve DOM'a eklendiğinde çağrılır:

  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()

Updating

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()

Unmounting

Kaldırmak

Bu fonksiyon, bir component DOM'dan kaldırıldığında çağrılır:

  • componentWillUnmount()

Error Handling

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()

Diğer API'ler

Her component aşağıdaki API'leride içerisinde barındırır:

  • setState()
  • forceUpdate()

Class Property

  • defaultProps

render()

render()

render() fonksiyounu zorunludur.

Çağrıldığında, this.props ve this.statei 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 testin boolean olduğu durumda return 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() fonksiyonu false return ederse render() çağrılmayacaktır.


constructor()

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.statee 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()

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()

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()

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()

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 truedur. 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.PureComponentten devralmak için değiştirebilirsiniz. Elle yazmak istediğinizden eminseniz, this.props ile nextProps ve this.state ile nextStatei 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()

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()

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()

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()

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()

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 propsun 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.


forceUpdate()

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.stateten okuma yapmalısınız.


Class Property

defaultProps

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

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.

ES6, ES2015, ES2016

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.

Compiler

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.

Bundler

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

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

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

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

Elementler

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.

Componentler

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

props bir React componentinde, bir üst componentten alt componentlere geçirilen verilerdir.

Unutmayın ki propslar yalnızca okunurdur; hiçbir şekilde değiştirilmemelidirler:

// Yanlış!
props.number = 42;

props.children

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

state

Bir Component, kendisiyle ilişkili bazı veriler zaman içinde değiştiğinde statee ihtiyaç duyar.

state ile props arasındaki en önemli fark, propsun 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ı

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.

Keys

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.


Ajax Kullanımı

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.

Lifecycleda AJAX isteğini nerede yapmalıyım?

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, statei doldurmak için componentDidMountta 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.


XOX Oyunu

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.


Verileri Props Üzerinden Geçirmek

Board componentinden Square componentine bazı verileri geçirmeyi deneyelim.

Board'un renderSquare fonksiyonunda, bir value propsu Squareye 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.

Mevcut kodu görüntüleyin

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.


Etkileşimli Component

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.statee 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.valueu this.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.

Mevcut kodu görüntüleyin


Veriyi Korumanın Önemi

Ö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.

Veriyi Doğrudan Değiştirmek

var player = {score: 1, name: 'Ömer'};
player.score = 2;
// player objesinin son hali: {score: 2, name: 'Ömer'}

Verinin Kopyasını Oluşturmak

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

Daha Kolay Geri Alma / Yeniden Yapmak

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.

Track Değişiklikleri

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ın Ne Zaman Yeniden Render Edileceğini Belirleme

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.

Fonksiyonel Componentler

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.onClicki çağırırdı.

Mevcut kodu görüntüleyin


Sıradaki Oyuncu

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

Mevcut kodu görüntüleyin


Kazananı Bildirmek

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 Boardun handleClickini 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 !

Mevcut kodu görüntüleyin