7 min read

React ile admin paneli yazımı

React ile Places API'ye erişerek okuma/yazma yapan bir yönetim paneli yazıyoruz.
React ile admin paneli yazımı

Önceki bir yazımda Places API adında bir uygulamayla veritabanında bulunan mekan verilerimizi başka uygulamalar tarafından HTTP metotları kullanarak okuma/yazma erişimine açmıştık.

https://pinkhatapps.com/entity-framework-core-3-1-ile-api-yazimi

Şimdi gelin bu API için bir React frontend yazarak kullanıcılarımızın web tarayıcısı kullanarak verileri okuyup yazabilmesini sağlayalım.

Uygulamanın tamamlanmış halini github'dan indirip çalıştırabilirsiniz.

gulangurman/places-admin
React administration panel for the Places API. Contribute to gulangurman/places-admin development by creating an account on GitHub.

1.Başlangıç

Öncelikle Visual Studio Code'da bir terminal açarak boş bir react uygulaması oluşturmakla başlıyoruz.

npm init react-app places-admin

Kontrol etmek için hemen çalıştırabiliriz:

cd places-admin
npm start

Tarayıcıda şu görseli görüyorsanız react uygulaması sorunsuz bir şekilde kurulmuş demektir.

Şimdi arka planda PlacesAPI uygulamasını çalıştırarak erişime açalım.

Tarayıcıda bütün mekanların listesini HTTP GET ile istediğinizde boş bir JSON array göreceksiniz. Bunun sebebi PlaceAPI şimdilik bir in-memory veritabanı kullanıyor. API yeniden başlatıldığında bütün veriler siliniyor.

Render, state, action, jsx ???

React'ta bir uygulama tasarlarken her zaman state ve action bağlamında düşünmek gerekir.

React uygulaması dinamik bir SPA'dır; yani aslında bütün site sadece tek bir html sayfasından ibarettir.  Bütün dinamizm javascript altyapısı tarafından sağlanır.

İşte bu yüzden ekrandaki sayfayı değiştirmek veya içeriğini güncellemek için javascriptle yazılmış olan componentler render edilir, yani anasayfanın html DOM ağacına eklenerek görseller ekrana yansıtılır.

Bu işlem yani DOM'a müdahale edilmesi işlemi performans, tutarlılık, vb. sebeplerle elle yapılması sıkıntılı olduğundan React kütüphanesine emanet ediyoruz.

*Bir component render edildiğinde ekrana vermek üzere bellekte sakladığı veriler onun state'i yani durumudur.

*Bu verileri yani ekranın durumunu değiştirmek için yaptığımız işlemler de birer action'dır.

*Bir componentin state'i/durumu değiştiğinde yeniden render edilir, böylece ekranda bu component içerisinde hep güncel verilerin görünmesi sağlanır.

2.Listeleme

Uygulamanın ana sayfasında veritabanında varolan mekanların listesini gösterelim.

Aklımda şöyle bir görüntü var:

Hedefimiz bu!

useEffect ile action tanımlayalım

Mekanlarımızı API'den getirmek için uygulamamızda useEffect hook kullanarak API'ye HTTP GET isteği gönderip, cevabını alacağız.

useEffect'in ikinci parametresini (dependencies) boş bırakmamız, bu efektin sadece ilk yüklemede çalışmasını sağlıyor. Aksi halde her render'da state'i güncelleyecek, state güncellendiği için de yeniden render edilecektir. Bu da sonsuz bir döngüye girildiği anlamına gelir. Bu konudaki kaynaklar biraz kafa karıştırıcı olabiliyor; çünkü şu anda yaptığımız gibi hook içerisinde API'dan veri alarak state'in güncellenmesi biraz özel bir durum.
useEffect(() => {
    fetch(API_URL + "/Places")
      .then((response) => response.json())
      .then(setApiResponse)
      .catch((error)=>console.log("GET /Places failed: " + error));
  }, []);
API'den gelen cevap json formatına dönüştürülüp, state güncelleniyor. Eğer hata olursa konsola mesaj yazılıyor.

useState ile state tanımlayalım

Alacağımız cevabı saklaması için useState hook kullanarak bir array tanımlayacağız.

const [apiResponse, setApiResponse] = useState([]);

JSX ile arayüzü dizayn edelim

İlk adım olarak veritabanında varolan bütün mekanların sayısını göstermekle başlayalım.

<header className="App-header">
        There are {apiResponse.length} Places in the database.
</header>

İlk kez çalıştıralım

App.js son hali:

import "./App.css";
import { useEffect, useState } from "react";

function App() {
  var API_URL = "http://localhost:5000";
  const [apiResponse, setApiResponse] = useState([]);
  useEffect(() => {
    fetch(API_URL + "/Places")
      .then((response) => response.json())
      .then(setApiResponse)
      .catch((error) => console.log("GET /Places failed: " + error));
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        There are {apiResponse.length} Places in the database.
      </header>
      <div>
        {apiResponse.forEach((item) => {
          <p>{item.name}</p>;
        })}
      </div>
    </div>
  );
}

export default App;

Şimdi çalıştırdığımızda ekranda mekan sayısını sıfır olarak görmemiz gerekir.

npm run start
API'ye yapılan HTTP GET isteği sonucunda henüz boş bir dizi geliyor. Eğer API tarafında CORS izni verilmemişse konsolda hata alırsınız.

JSX ile bir liste yapalım

Şimdi bütün mekanların isimlerini listeleyen bir jsx kodu yazalım. İleriki aşamalarda bunların resimlerini, detaylarını, etiketlerini, vb. ekleyerek şık bir görsel oluşturabilirsiniz.
Burada javascript'teki map fonksiyonundan yararlanıyoruz. API'den gelen dizideki her bir mekan nesnesi için ayrı bir paragraf oluşturarak içerisinde mekan ismini gösteriyoruz.

<div>
        {apiResponse.map(item => <p>{item.name}</p>)}
</div>

App.js son hali:

import "./App.css";
import { useEffect, useState } from "react";

function App() {
  const API_URL = "http://localhost:5000/api";
  const [apiResponse, setApiResponse] = useState([]);
  useEffect(() => {
    fetch(API_URL + "/Places")
      .then((response) => response.json())
      .then(setApiResponse)
      .catch((error) => console.log("GET /Places failed: " + error));
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        There are {apiResponse.length} Places in the database.
      </header>
      <div>
        {apiResponse.map(item => <p>{item.name}</p>)}
      </div>
    </div>
  );
}

export default App;

Şu anda çalıştırdığımızda veritabanında henüz hiçbir mekan bulunmadığı için liste oluşmayacaktır. O yüzden vakit kaybetmeden yeni mekan ekleme bölümüne geçelim.

2.Ekleme

Uygulamanın ana sayfasında bir kutucuk koyarak veritabanına yeni mekan eklenmesini sağlayabiliriz.

React ile form yazarken hazır bir kütüphane kullanmanızı öneririm. Bunun sebebi react formlarının sıradan html formlarından farklı olması. React ile yazdığınız HTML input elementlerinin değerlerinin React tarafından state'te tutulması gerekiyor. Unutmayın: asp.net'de bir html formunu doldurduğumuzda postback ile değerler sunucuya gider, React'ta ise form zaten dinamik olarak oluştuğundan, doldurup post ettiğimizde de ne olacağını elle tanımlamamız gerekiyor. Böyle olunca da formlarda state güncelleme, validation, hata mesajları, vb. elle yazmak bir yerden sonra uzun ve sıkıcı bir uğraşa dönüşüyor. Ama hazır kütüphaneler isteklerinizi karşılamıyorsa veya öğrenme amaçlı kendiniz de yazabilirsiniz.

Formu jsx ile yazalım. Başlangıç olarak bir mekan ismi yazılıp gönderelim.

<form onSubmit={addPlace}>
    <label>Mekan adı:</label>
    <input
            type="text"
            name="placeName"
            onChange={(e) => setPlaceName(e.target.value)}
    />
    <input type="submit" />
</form>

Formdaki her bir input elementi için ayrı bir state tutulması ve güncellenmesi gerekir. Bu amaçla useState hook kullandık.

const [placeName, setPlaceName] = useState("");

Ayrıca form submit edildiğinde HTTP POST isteği gönderilecek bir de addPlace() fonksiyonu yazalım:

function addPlace() {
    fetch(API_URL + "/Places", {
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        name: placeName,
        tags: "",
        isOpen: false,
      }),
      method: "POST",
    })
      .then((response) => response.json())
      .then(() => console.log("POST /Places success!"))
      .catch((error) => console.log("POST /Places failed: " + error));
  }

Şimdi formu kullanarak bir mekan ismi girdiğimizde mekan sayısının güncellendiğini görebiliriz.

Konsolda da API'ye gönderilen isteğin içeriğini (payload) ve karşılığında gelen 201 Created cevabını inceleyebilirsiniz. Bu cevapla beraber sunucu Location header'da bu mekanın url'sini de bildirmiş.

Listelemeye tekrar dönelim.

Listede mekanın açık/kapalı durumu görülebilsin. JSX ile bunu ternary operator ile kısa bir if-else ifadesi gibi yazabiliyoruz. Ayrıca mekanlara sıra numarası vermek için başlarında index değerini de gösterebiliriz.

<div>
    {apiResponse.map((item, index) => {
          return (
            <p>
              {index + 1}. {item.name} : Şu an {item.isOpen ? "açık" : "kapalı"}
              .
            </p>
          );
    })}
</div>

Burada return ifadesini kullanmayıp kısa versiyonu da tercih edebilirsiniz.

{apiResponse.map((item, index) => (
    <p>
        {index + 1}. {item.name} : Şu an {item.isOpen ? "açık" : "kapalı"}.
    </p>
))}

Şimdi yeni eklediğimiz mekanlar ile oluşan listeyi görebiliriz.

Listede mekan isimleri görülüyor. Yalnız konsolda "key" prop hakkında bir uyarı mesajı var.

Key ekleyelim.

React'ın dinamik olarak verileri yönetmesinden dolayı güncellemeleri takip etmek amacıyla nesneleri ayrı ayrı seçebilmesi gerekir. Yani bir listeniz varsa, bu listenin her bir elemanına birer unique key vermelisiniz. Bu key değerine genellikle nesnenin ID'si verilir.

<div>
        {apiResponse.map((item, index) => (
          <p key={item.id}>
            {index + 1}. {item.name} : Şu an {item.isOpen ? "açık" : "kapalı"}.
          </p>
        ))}
</div>

Şimdi çalıştırdığımızda konsolda uyarı mesajı görülmeyecek.

Listeye key verdikten sonra uyarı mesajı görünmüyor.

Son söz.

Bu yazıda sizlerle http GET ve POST istekleri göndererek React frontend üzerinden bir API'ye okuma/yazma işlemleri yaptık. Devam yazısında http DELETE ve PUT istekleri ile silme ve güncelleme yaparak, tam anlamıyla CRUD işlemlerini yapabilen bir yönetim ekranı oluşturmuş olacağız.

React ile çalışırken bazı şeylerin alıştığınızdan daha kolay, bazılarının da daha zor olduğunu fark edeceksiniz. Bunun sebebi state managament dediğimiz kullanıcı verilerinin yönetiminin sunucudan alıp istemci tarafına getirilmiş olması.

Yani daha önce sunucunun üzerinde olan işlem yükünün önemli bir kısmını, verileri kaynağından getirip sayfaları oluşturma kısmını, tarayıcıya emanet ediyoruz. Haliyle geliştiriciler olarak bunları yönetecek kodu tamamen elle yazmamız gerekiyor.

React'a yeni başlayanların bunu dikkate alması çok önemli. Yani sadece ASP.NET MVC ile yapılabilecek bir web uygulamasını sırf moda oldu diye react ile baştan yazmak size bir şey kazandırmayabilir. Ama react ile frontend yazdığınızda artık dilediğiniz gibi API erişimi sağlayarak, aldığınız verileri işleyen görselleri -sunucuyu hiç işe karıştırmadan- programlayabilirsiniz. Seçim tamamen size kalmış.

Bir sonraki yazıda görüşmek üzere, hoşçakalın.