React 101: React’a Giriş, React Anlatım
Geçenlerde şirkette yaptığım sunumu buradan da paylaşmak istedim. Sunum en başta teorik olarak başlıyor devamında pratikler yapıyoruz. Bu sunumdan sonrasını uygulamalı, videolu React ile proje geliştirme şeklinde yapmak istiyorum.
REACT NEDİR?
ReactJS Kullanıcı arayüzleri oluşturmaya yarayan bir JavaScript kütüphanesidir. MVC (Model-View-Controller) içindeki V (View) ye karşılık gelir.
https://facebook.github.io/react/
TEORİK: REACT TARİHÇE
React’in tarihçesinden hızlıca bahsedecek olursak, Facebook Instagram’ı bünyesine kattığında Instagram’ın bir web sitesi yoktu, sadece mobil uygulamaları vardı. Bir web sitesi geliştirmek isteyen Instagram mühendisleri Facebook’un yorumlar, reklamlar gibi bazı bileşenlerinde kullandığı dahili kütüphaneyi kullanmak istedi.
Yapılan çalışma sonucu kütüphanenin Facebook kod tabanındaki diğer bağımlılıkları kaldırıldı ve kütüphane 2013 Mayıs’ında JSConf’ta açık kaynak olarak dünyaya tanıtıldı. React.js’e ilk başta oldukça şüpheyle yaklaşıldı, hatta tanıtıldığı konferansta Facebook’un piyasadaki bütün bilinen doğruları sorguladığı, JS ile HTML’i çorba yaptığı şeklinde alay konusu bile oldu. Ancak sonraki dönemde bu kütüphanenin karmaşık sistemleri basitleştirdiği ve hızlandırdığı birçok firma tarafından fark edildi ve Netflix, Airbnb, Khan Academy gibi pek çok firma arayüzlerinde React.js’e geçti.
TEORİK: BÜYÜK UYGULAMALARDA ARAYÜZ GELİŞTİRMENİN ZORLUKLARI
React’in temel hedefini şu şekilde özetleyebiliriz: Verinin zaman içerisinde değiştiği büyük uygulamaları basit ve hızlı bir şekilde geliştirmek.
Çoğu durumda arayüz geliştirmek oldukça zordur, peki buradaki asıl zorluk nedir? Örneğin sunucu tabanlı bir web sistemi geliştirdiğimizi düşünelim. Veritabanından verileri çektik, daha sonra bir taslak, yani template aşamasından geçirip HTML çıktısını oluşturduk. Çoğu uygulamada, örneğin PHP’de, Django’da ya da Rails’te bu oldukça kolay bir işlemdir. Bu işlemi veriyi alan ve sonuç olarak HTML üreten bir fonksiyon olarak soyutlayabiliriz.
Halbuki işler istemci tabanlı, yani clientside bir arayüz geliştirmeye geldiğinde oldukça zorlaşır. Buradaki zorluğun sebebi kullanılan dil olan JavaScript midir?
Aslında birkaç yıl öncesine kadar çoğu kişinin kanısı bu yöndeydi. Bilindiği gibi JavaScript dili Brendan Eich tarafından yalnızca 10 gün içinde, deyim yerindeyse yangından mal kaçırırcasına tasarlanmış bir programlama dili. Bu nedenle dilde hala giderilmeye çalışılan bazı hatalar ve gariplikler var.
Ancak Douglas Crockford’un JavaScript the Good Parts kitabında anlattığı üzere bu 10 gün içerisinde Brendan Eich, görüntü olarak C’ye benzese de altında bir Lisp yatan, çok esnek, fonksiyonel bir dil yaratmıştı. Zaten dildeki hatalar da CoffeeScript, ES6 gibi diller aracılığıyla ve underscore gibi fonksiyonel kütüphaneler sayesinde zaman içerisinde oldukça azaltıldı.
Peki arayüz geliştirmedeki ana zorluk dil değilse nedir? Büyük uygulamaları incelediğimizde ve sunucuda oluşturulan arayüzleri tarayıcıdaki arayüzlerle karşılaştırdığımızda asıl zorluğun zaman içerisinde değişen veri yönetimi olduğunu görürüz. Sunucu taraflı bir uygulamayı düşünelim, veriyi veritabanından çektik, taslağa gönderdik. Bu esnada veriyi aslında değişmeyen bir yapı olarak düşünebiliriz.
Ancak istemci tarafında işler bu şekilde ilerlemez. Diyelim kullanıcı tarayıcıyı açtı, uygulamayı başlattı; işte bu noktadan sonra uygulamadaki veri sürekli değişim halindedir ve uygulamadaki görüntü ile verinin senkronizasyon sorunu başlar. Hele aynı veri görüntünün birden fazla yerinde gösteriliyorsa işler daha da karmaşıklaşır.
Örneğin bir chat uygulaması düşünelim. Şu an çevrimici olan arkadaşlarımızı ve toplam kaç arkadaşımızın çevrimiçi olduğunu göstersin. Diyelim ki bir arkadaşımız daha çevrimiçi hale geldi. Burada herhangi bir arayüz kütüphanesi kullanmıyorsak yapmamız gereken iki değişiklik var: Öncelikle yeni gelen arkadaşımızın ismini listeye eklemeli, daha sonra da tepedeki arkadaş sayısını bir artırmalıyız. Yani bir anlamda görüntüde kısmi bir değişiklik, bir yama yapmalıyız. Bu basit bir örnek gibi görünse de arayüz geliştirmedeki sorunların birçoğu işte bu veri ve görüntü arasındaki köşe kapmaca yüzünden yaşanmakta; veri değiştiğinde programcı görüntüdeki bir veri değiştirmeyi unutmakta.
TEORİK: REACT’IN TEMEL FELSEFESİ: GÖRÜNTÜYÜ HER SEFERİNDE YENİDEN OLUŞTURMAK
İşte bu noktada React’in arayüz oluşturmadaki birinci ve temel felsefesi devreye giriyor: Verideki her değişimde görüntüyü yamamak yerine sil baştan oluşturalım. Yani görüntü yapacağımız değişiklikleri düşünmek yerine sadece veride bir değişiklik yapalım, görüntü sanki sayfayı yeniden yüklemişiz gibi sıfırdan oluşturulsun. Bu şekilde veri ve görüntü arasında hiçbir zaman bir anlaşmazlık olmasın.
Tabii kulağa çok hoş geliyor, ama bu konuda kafanızda bazı soru işaretleri olmuş olabilir: Diyelim 100 kişilik bir listemiz var, veriye bir kişi daha ekledik, 101 kişilik yeni bir liste oluşturmamız gerekmekte. Bunu naif bir şekilde yaparsak, elimizde halihazırda bulunan 100 kişilik listeyi çöpe atacağız ve sıfırdan 101 kişilik bir liste oluşturacağız. Bu oldukça yavaş olmaz mı?
React’in kütüphane olarak devreye girdiği yer tam da bu nokta. React, kullanıcı olarak sizin sadece veriye ve o verinin nasıl görüntüleneceğine odaklanmanızı sağlarken arkaplanda görüntüdeki değişimin en etkin biçimde yapılmasını sağlamakta. Yani 101 kişilik yeni listeyi oluştururken sizin yerinize görüntüde yapılması değişiklikleri hesaplamakta ve sadece bu değişiklikleri görüntüye uygulamakta. İşte React’in bunu yaparken kullandığı yönteme de sanal DOM (virtual DOM) diyoruz. React görüntü elemanlarını sanal DOM adı verilen bir veri yapısında tutuyor ve değişiklik olduğunda bu veri yapısını güncelleyerek görüntüde toplu değişiklikler yapiyor. Bu yöntemle React verideki değişiklikler sonucunda optimum olarak hangi HTML elemanlarının eklenip çıkarılacağına kendisi karar veriyor.
Dolayısıyla React’in birinci temel kuralını şu şekilde özetleyebiliriz: Kullanıcı temel olarak arayüzün arkasındaki veri yapısına ve değişikliklerine odaklanmalı ve bu verinin nasıl görüntüleneceğini yalnızca bir kez tanımlamalı.
TEORİK: REACT’IN İKİNCİ NESİL İLKESİ: BİLEŞENLER
Şimdi gelelim React’in ikinci temel ilkesine. React’e göre arayüz geliştirirken temel hedefimiz aslında bileşen oluşturmak olmalı; yani elimizdeki arayüzü bileşenlere ayırmalı ve bileşenler cinsinden analiz etmeliyiz. Elimizde halihazırda bir HTML taslağı olduğu düşünelim, örneğin bir adres defteri. Bunu gerçekleştirmek için bunu küçük bileşenlere ayıralım. Örneğin tepedeki bileşen bir arama bileşeni, aşağıdaki her bir harfe ait kişiler bir Sayfa
bileşeni, her bir adres satırı ise birer Adres
bileşeni olarak düşünülebilir.
İşte bu yaptığımız temelde bir soyutlama işlemi, elimizdeki küçük Lego bloklarından daha büyük Lego blokları yapıyoruz ve en nihayetinde en büyük Lego bloku olan uygulamamızı kuruyoruz.
Bunu programlamada birden fazla cümleden fonksiyon oluşturmaya benzetebiliriz. Bu fonksiyonları kullanan yeni üst fonksiyonlar oluşturduğumuz gibi bileşenleri kullanan üst bileşenler oluşturabiliriz. Bu şekilde elimizde yeniden kullanılabilir (reusable), birbiri içine geçebilen (composable), nasıl kullanılacağı tanımlanmış küçük ve hatasız birimler oluşacak. React terminolojisinde temelde kullanılan bu yapıya component, yani bileşen denmekte.
React önyüzü (view), komponent olarak isimlendirir. Temel fonksiyonlar React isimli nesne üzerinden dışarıya açılır.
SANAL DOM (VIRTUAL DOM)
DOM’a ne kadar müdahale edilirse o kadar maliyet artar. React gerçek bir DOM elemanını temsilen bir Sanal DOM elemanı tutar. Böylece gerçek DOM’a az dokunarak maliyetleri azaltır.
DOM farkını gözetme (DOM diff).
DOM elemanına gerekmedikçe dokunulmaz(mutate).
Toplu güncelleme ve silme. Kaçınılmaz DOM işlemleri toplu yapılır.
KURULUM
React kurulumu oldukça basit. Aşağıdaki komutları çalıştırarak hızlıca bir app yaratabilirsiniz.
npm install -g create-react-app create-react-app my-app cd my-app npm start
YARARLI NESNELER
- React.createElement: DOM elemanı oluşturur
- React.Component: DOM eleman kümesi (komponenti) oluşturur
- ReactDOM.render: Bir React elemanını veya komponentini gerçek DOM’a dönüştürür.
React.createElement
const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' );
React.Component
Birçok React elemanını gruplar ve yönetir. React.createClass fonksiyonu bir React komponenti oluşturur. Oluşan bileşen aslında bir Virtual DOM elemanıdır.
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
ReactDOM.render
const element = <h1>Hello, world</h1>; ReactDOM.render( element, document.getElementById('root') );
YENİ BİR KOMPONENT
class Merhaba extends React.Component { render() { return React.createElement("div", null, React.createElement("h3", null, "Merhaba React"), React.createElement("p", null, "Selam") ); } } ReactDOM.render( <Merhaba/>, document.getElementById('root') );
Buradaki null props olmadığı anlamına gelir. *type, props, children
JSX NEDİR?
JavaScript içinde React bileşen ağacını temsil eden XML yapılı bir teknolojidir. Javascript içerisine html yazmayı kolaylaştırır.
JSX ⇒ (Transpilers) ⇒ JS
https://facebook.github.io/jsx/
Bunu:
class MyComponent extends React.Component { render() { return React.createElement('div', null, 'Merhaba Enuygun'); } } ReactDOM.render( React.createElement( MyComponent ), document.getElementById('root') );
Aşağıdaki şekilde yazmamıza imkan verir.
class MyComponent extends React.Component { render() { return <div>Merhaba Enuygun</div>; } } ReactDOM.render( <MyComponent />, document.getElementById('root') );
JSX KURALLARI 1
Açılan eleman mutlaka kapatılmalı.
<eleman></eleman> // doğru <Eleman/> // doğru <Eleman> // yanlış
JSX KURALLARI 2
Alt elemanlar tek bir eleman içinde sarmalanmalı.
// doğru (<eleman> <eleman1/> <eleman2/> </eleman>) //yanlış ( <eleman1/> <eleman2/> )
JSX KURALLARI 3
JSX elemanları HTML attribute barındırabilir.
İstisnalar
class ⇒ className
for ⇒ htmlFor
(<div id="hello"> <h1>Merhaba Dünya</h1> <p> <a href="http://enuygun.com">enuygun.com</a> </p> </div>)
YENİ BİR KOMPONENT
Değişken değerleri iki süslü parantez { } arasında bileşene aktarılabilir.
class Merhaba extends React.Component { render() { const sinif = "merhaba"; const icerik = "Merhaba Dünya"; return ( <p id="merhaba" className={sinif}> {icerik} </p> ); } }); ReactDOM.render( <Merhaba/>, document.querySelector("#icerik") );
BİR KOMPONENTİN TEKRAR KULLANILMASI
const Name = React.createClass({ render: function () { return ( <strong>Evren</strong> ); } }); const Surname = React.createClass({ render: function () { return ( <u>Akar</u> ); } }); const FullName = React.createClass({ render: function () { return ( <p> <Name/> <Surname/> </p> ); } });
PROPS (STATELESS DATA)
Sanal DOM’un kullanım amacı çeşitli tekniklerle render maliyetinin azaltılmasıdır. Bu yüzden React bileşenleri, değişmeyen önyüz verileri için props (properties) adında bir alana sahip.
PROPS 1
Bu veriler sonradan değiştirilmesi beklenmeyen verilerdir.
const StrongDom = React.createClass({ render: function () { return (<strong>{this.props.content}</strong>); } }); ReactDOM.render( <StrongDom content="Merhaba Dünya"/>, document.querySelectorAll(".placeholder")[0] ); const content = "Merhaba Uranüs"; ReactDOM.render( <StrongDom content={content}/>, document.querySelectorAll(".placeholder")[1] );
PROPS 2
İlk container classına ne yazdırır?
İkinci container classında sonuç ne olur?
const ProgressBar = React.createClass({ render: function () { const color = this.props.color; const position = this.props.position || 100; return ( <div className={"progress " + color} role="progressbar" /> <div className="progressmeter" style={{width: position}} /> );} }); const container = document.querySelectorAll(".container"); //1// ReactDOM.render( <ProgressBar position="25"/>, container[0] ); //2// ReactDOM.render( <ProgressBar position="50" color="warning"/>, container[1] );
Evet ilk container classında undefined yazmasını bekliyorsunuz sanırım değil mi? İkincisinde de “progress warning”.
Ancak sonuç olarak kodunuz derlenmeyecektir. Çünkü yukarıdaki React elementinin return kısmında sarmallanmamış 2 adet div bulunuyor. React size bunları kapsayan bir div daha koymanızı önerecek ve hata verecektir.
STATE (STATEFUL DATA)
React mimarisinde state değişebilir veriler için kullanılmaktadır. React state değiştiğinde bileşen otomatik olarak yeniden render edilir.
STATE-1
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); } tick() { this.setState({ date: new Date() }); } render() { return ( <div>It is {this.state.date.toLocaleTimeString()}</div> ); } }
STATE-2
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); } tick() { this.state.date = new Date(); } render() { return ( <div>It is {this.state.date.toLocaleTimeString()}</div> ); } }
OLAY YÖNETİMİ #1
Event | React |
---|---|
onclick | onClick |
onkeypress | onKeypress |
ondblclick | onDblclick |
OLAY YÖNETİMİ #2
class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } render() { return ( <button onclick="{this.handleClick}"> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } }
YAŞAM DÖNGÜSÜ (LIFECYLE METHODS)
Evre | Açıklama |
---|---|
getDefaultProps | Bileşenin kullanacağı değişmeyen verilerin başlangıcını döndürür |
getInitialState | Bileşenin kullanacağı değişen verilerin başlangıcını döndürür. |
Evre | Açıklama |
---|---|
componentWillMount | Bileşen gerçek DOM’a bağlanmadan hemen önce bu fonksiyon çağrılır. |
render | Bileşen güncellenirken render fonksiyonu çağrılır. |
componentDidMount | Bileşen gerçek DOM’a bağlandıktan hemen sonra bu fonksiyon çağrılır. |
Evre | Açıklama |
---|---|
shouldComponentUpdate | Bileşenin güncellenip güncellenmeyeceğine burada karar verilir. True / False |
componentWillUpdate | Bileşen güncellenmeden hemen önce bu fonksiyon çağrılır. Bileşen güncellenirken render fonksiyonu çağrılır. |
componentDidUpdate | Bileşen güncellendikten hemen sonra bu fonksiyon çağrılır. |
REFERENCES
Bazı durumlarda DOM elemanlarına React içinde erişmemiz gerekebilir. Bu gibi durumlarda refs özelliğinden faydalanabiliriz.
class CustomTextInput extends React.Component { constructor(props) { super(props); this.focusTextInput = this.focusTextInput.bind(this); } focusTextInput() { ReactDOM.findDOMNode(this.refs.myInput).focus(); } render() { return ( <div> <input type="text" ref="myInput"> <input type="button" onclick="{this.focusTextInput}"> </div> ); } }
VERİ DOĞRULAMA
Proje ölçeği büyüdükçe veri alanlarının tip bilgisini bilme ihtiyacı masrafı artar. Bunu propTypes özelliği ile giderebiliriz.
import PropTypes from 'prop-types'; class Greeting extends React.Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } } Greeting.propTypes = { name: PropTypes.string };
DİĞER ÖNEMLİ KONULAR
Reactjs konusunda diğer önemli konular olarak şunları sayabiliriz:
- Geliştirme aşamasında React DevTools çok büyük kolaylık getirmekte, HTML DOM yapısına benzer şekilde bileşen ağacının görülmesine, bileşenlerin props ve state değişkenlerinin incelenmesi ve değiştirilmesine olanak vermekte.
- node.js kullanarak sunucu tarafında React çalıştırmak suretiyle isomorphic denilen hem sunucu, hem istemci tarafında çalışan uygulamalar geliştirilmesi. Burada
React.render
yerine HTML çıktısını string olarak oluşturanReact.renderToString
kullanılmakta ve bu uygulamalar sayesinde daha hızlı görüntülenen uygulamalar yapılabilmekte. - Yeni duyurulan React Native ile bu yapının iPhone ve Android geliştirmesinde kullanılması mümkün hale geldi. Burada PhoneGap tarzı HTML’in mobil uygulamaya gömülmesi değil, doğrudan native (yerel) uygulamalar geliştirilmesi sözkonusu.
- React uygulamaları daha büyüdükçe, veri yönetimi için yine Facebook’un duyurduğu Flux kütüphanesi kullanılmakta. React’te değişken verinin giderek yukarı çıktığını gördük, bunun bir sonraki adımı verinin tamamen bileşenlerin dışında Store (depo) denilen bir yapıda tutulması ve bileşenlerin bu Store’lardaki verilere üye olması. Store’lardaki değişimlerin de bileşenlerden gelen action (hareket) denilen değişiklik haberlerine göre yapılması.
SONUÇ
Sonuç olarak React’in 3 temel özelliği olduğunu gördük. Birincisi veri değişimlerinin her zaman açık seçik yapıldığı ve verinin tek taraflı olarak aktarıldığı. İkincisi uygulamanın bileşenlere ayrıldığı ve bu bileşenlerin verilerini tepeden props, kendi içinde state şeklinde yönettiği ve yukarıda bir değişiklik yapılacaksa bu değişikliklerin callbackler ile yapıldığı. Üçüncüsü veri ile görüntü senkronizasyonu için bileşenlerin görüntüsünün her zaman baştan oluşturulduğu, React’in de sanal DOM adı verilen metodla bunu çok hızlı bir şekilde yaptığı. Bu özellikler sayesinde React kütüphanesi ile yüksek performanslı arayüzleri hızlı ve hatasız şekilde geliştirebilmekteyiz.
Evet arkadaşlar React’a giriş kısmı bu kadar. Bundan sonraki kısmı videolu anlatım ve bir proje geliştirme şeklinde yapmayı düşünüyorum. Konu olarak belki biraz Redux, Immutable.js ve next.js e değiniriz ve onlarla ilgili örnekler de yaparız. Hoşçakalın.
React ile ilgili diğer konular:
Yorum yap