Javascript Günlüğü: React'a Giriş

React, Facebook ekibi tarafından geliştirilen, Javascript ile kullanıcı arayüzlerini tasarlayabileceğiniz bir kütüphanedir. Popüler geliştirme desenlerinden biri olan MVC'nin View kısmına benzetebiliriz.

React is a JavaScript library for creating user interfaces by Facebook and Instagram. Many people choose to think of React as the V in MVC.

We built React to solve one problem: building large applications with data that changes over time.

Kaynak: https://facebook.github.io/react/docs/why-react.html

Neden React?

React'ın en önemli özelliği "Reactive Update". Uygulamadaki state değiştikçe yeni yapıyı kendi tuttuğu Virtual DOM ile karşılaştırarak sadece değişen kısımları güncelliyor. Böylece geliştirme yaparken UI güncellemelerini düşünmenize gerek kalmıyor, bu kısmı kendisi hallediyor.

Öğrenmesi ve geliştirmesi oldukça basittir, büyük çaplı projelerde işleri kolaylaştırdığını söyleyebilirim. Şu anda Facebook'un bir kısmı ve Instagram'ın ise tamamı React ile çalışıyor.

Detaylara girmeden önce belirtelim, kodlara Github üzerinden erişebilirsiniz.

JSX Nedir?

JSX işleri kolaylaştıran syntax eklentisidir. JSX ile HTML elementlerini kolayca oluşturabilirsiniz. Bir örnek ile açılayalım.

JS

React.createElement("div", {className: "div-class"}, "Hello World!");

JSX

<div className="div-class">Hello World!</div>

Hello World

React kullanmanın bir kaç yolu var, doğrudan sayfaya React ve Babel kodunu dahil edip, JSX kullanarak yazmaya başlayabilirsiniz veya yazdığınız kodu Babel ile çevirerek daha hızlı bir şekilde kullanabilirsiniz.

Aşağıdaki örnekte Babel'i browser üzerinde kullanarak JSX kodunu çalıştırıyoruz.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>React Tutorial</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react-dom.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  </head>
  <body>
    <div id="content"></div>
  </body>
  <script type="text/babel">
    ReactDOM.render(
      <h1>Hello, world!</h1>,
      document.getElementById('content')
    );
  </script>
</html>

ReactJS / Hello World

Terminal Üzerinden Derlemek

Yukarıkdaki örnek Babel'in browser üzerindeki kullanımını anlatıyordu, başlangıç için bu yöntem kullanılabilir fakat ilerledikçe yavaşlıklar hissedeceksiniz. Dolayısıyla projenizi Babel ile terminal üzerinden derlemenizi öneririm. Yazının devamında bu yöntem kullanılacaktır.

React ve Babel kurulumu için sisteminizde NodeJS olmalı. Aşağıdaki NPM komutları ile kurulumu yapabilirsiniz.

npm install -g react react-dom
npm install -g babel

Component, State ve Prop

Basit bir sayaç örneği ile başlayalım. React modüler bir yapıya sahip, yaptığımız her şeyi component yapılarına bölmek işi kolaylaştıracaktır.

// src/main.jsx

var App = React.createClass({
  getInitialState: function(){
    return{
      counter: 0
    }
  },
  increaseCounter: function(){
    this.setState({
      counter: this.state.counter + 1
    });
  },
  render: function(){
    return(
      <div>
              {this.props.helloWorldString}
                <button onClick={this.increaseCounter}>Add +1</button><br />
        Counter: {this.state.counter}
      </div>
    )
  }
});

ReactDOM.render(
  <App helloWorldString="Hello World! This is first component." />,
  document.getElementById('content')
);

createClass ile component oluşturuyoruz. render ekrana basılan kısmı ifade ediyor, getInitialState fonksiyonunun döndürdüğü nesne ise başlangıç değişkenlerini içeriyor. Biraz önce bahsettiğimiz gibi state değiştikçe, değişen kısımlar güncellenir.

Her component kendi state değerini barındırır. Bu değer mutable (değiştirilebilir) olup setState ile değiştirilir. State değiştiği zaman component güncellenir.

Prop değeri, state'in aksine immutable nesnelerdir. Component'e dışarıdan gönderilen değerler diyebiliriz. Proplara gönderildiği component içinde this.props özelliği altından ulaşabilirsiniz.

Kodu Babel ile çevirelim.

babel src --watch --out-dir dist

Yukarıdaki komut sonucunda src dizinin altındaki .jsx uzantılı dosya derlenerek dist dizini altına .js uzantısı ile kaydedilir ve şu şekilde bir kod oluşur;

var App = React.createClass({
  displayName: "App",

  getInitialState: function getInitialState() {
    return {
      counter: 0
    };
  },
  increaseCounter: function increaseCounter() {
    this.setState({
      counter: this.state.counter + 1
    });
  },
  render: function render() {
    return React.createElement(
      "div",
      null,
      this.props.helloWorldString,
      React.createElement(
        "button",
        { onClick: this.increaseCounter },
        "Add +1"
      ),
      React.createElement("br", null),
      "Counter: ",
      this.state.counter
    );
  }
});

ReactDOM.render(React.createElement(App, { helloWorldString: "Hello World! This is first component." }), document.getElementById('content'));

Statik sayfayı host edebilmek için Python ile gelen basit HTTP sunucuyu kullanabilirsiniz. Proje dizininde aşağıdaki kodu çalıştırın.

python -m SimpleHTTPServer 8080

Birden Fazla Component

Birden fazla component'i bir arada kullanabilmek için biraz daha büyük bir uygulama yapalım. İş listesi (to-do list) uygulaması yapabiliriz. Uygulamanın yapısı şu şekilde olsun.

App
  - Input box
  - List box
    - To-Do item
// main.jsx

var InputBox = React.createClass({
  render: function() {
    return (
      <div>
        <input onKeyDown={this.props.callbackFunction} type="text" placeholder="Write something..." />
      </div>
    );
  }
});

InputBox componentine prop olarak bir callback fonksiyonu gönderiyoruz. Böylece alt component üzerinden addItem fonksiyonunu çağırıp listeye yeni bir iş ekleyebiliriz.

var ListBoxItem = React.createClass({
  render: function(){
    return(
      <li>
        {this.props.text} 
      </li>
    )
  }
});

var ListBox = React.createClass({
  render: function(){
    var list = this.props.data.map(function(item, i){
      return (
        <ListBoxItem text={item} key={"Item"+i}/>
      )
    });
    return (
      <ul>
        {list}
      </ul>
    );
  }
});

ListBoxItem en küçük birim oluyor. ListBox ise iş listesinin oluşturulduğu bölümdür. Gelen prop değiştikçe liste dinamik olarak güncellenir. Buradaki Map metodu kısaca verilen dizini işler ve ListBoxItem componentlerinden oluşan bir dizine çevirir.

var App = React.createClass({
  getInitialState: function(){
    return{
      items: []
    }
  },
  addItem: function(event){
    if(event.which === 13){
      this.setState({
        items: [...this.state.items, event.target.value]
      });
    }
  },
  render: function(){
    return(
      <div>
        <InputBox callbackFunction={this.addItem} />
        <ListBox data={this.state.items} />
      </div>
    )
  }
});

ReactDOM.render(
  <App />,
  document.getElementById('content')
);

items: [...this.state.items, event.target.value] satırı biraz garip gelebilir. Burada ES6'nın nimetlerinden yararlandık ve concat kullanmaktan kurtulduk :) Son olarak InputBox ve ListBox componentlerini App componenti üzerinde toplayarak content elemanı içinde ekrana bastırdık.

Unutulmaması Gerekenler

1) Birden fazla elemanı kapsayıcı bir eleman olmadan render etmeyi denemeyin, çalışmayacaktır.

One limitation: React components can only render a single root node. If you want to return multiple nodes they must be wrapped in a single root.

Kaynak: https://facebook.github.io/react/docs/displaying-data.html#components-are-just-like-functions

2) Production için kodu minimize edin, bunu çeviriciye -p seçeneceğini ekleyerek yapabilirsiniz.

babel src -p --out-dir dist

Örnek Kodlar

Kodlara Github üzerinden erişebilirsiniz: https://github.com/yildizberkay/javascript-gunlugu

Kaynaklar