Ders İçeriği

State Nedir? Neden Kullanılır?

State (durum), React bileşenlerinin dinamik verilerini saklamak ve yönetmek için kullanılan temel bir kavramdır. State, bir bileşenin içindeki değişebilen verileri temsil eder ve bu veriler değiştiğinde bileşen otomatik olarak yeniden render edilir.

State'in temel özellikleri:

Değişebilirlik: State verileri zaman içinde değişebilir. Kullanıcı etkileşimleri, API çağrıları veya zamanlayıcılar gibi olaylar sonucunda state güncellenebilir.

Yerellik: Her bileşenin kendi state'i vardır ve bu state sadece o bileşen tarafından doğrudan değiştirilebilir. Bu, veri yönetimini daha öngörülebilir hale getirir.

Reaktivite: State değiştiğinde, React otomatik olarak bileşeni yeniden render eder ve kullanıcı arayüzü güncel verilerle güncellenir.

Neden State Kullanırız?

State olmadan, React bileşenleri sadece statik içerik gösterebilir. Ancak gerçek dünya uygulamalarında, kullanıcı etkileşimlerine yanıt vermek, form verilerini yönetmek, API'lerden gelen verileri göstermek gibi dinamik işlemler yapmamız gerekir. İşte bu noktada state devreye girer.

Örneğin, bir sayaç uygulaması düşünün. Sayaç değeri değişmeli ve bu değişiklik ekranda görünmelidir. State kullanmadan bu mümkün değildir:

// State olmadan - Bu çalışmaz!

function Counter() {

  let count = 0; // Bu değer değişse bile bileşen yeniden render edilmez

  

  return (

    <div>

      <p>Sayaç: {count}</p>

      <button onClick={() => count++}>Artır</button>

    </div>

  );

}

State Kullanımı (useState Hook'u)

Modern React'te, fonksiyonel bileşenlerde state yönetimi için useState Hook'u kullanılır. Bu Hook, state değişkeni ve bu değişkeni güncellemek için bir fonksiyon döndürür.

Temel useState Kullanımı

import React, { useState } from 'react';


function Counter() {

  // useState Hook'u ile state tanımlama

  const [count, setCount] = useState(0);

  

  return (

    <div>

      <p>Sayaç: {count}</p>

      <button onClick={() => setCount(count + 1)}>Artır</button>

      <button onClick={() => setCount(count - 1)}>Azalt</button>

      <button onClick={() => setCount(0)}>Sıfırla</button>

    </div>

  );

}

useState Hook'unun yapısı:

İlk parametre: State'in başlangıç değeri

Döndürdüğü değerler: Bir dizi içinde iki element

- İlk element: Mevcut state değeri

- İkinci element: State'i güncellemek için kullanılan fonksiyon

Farklı Veri Tipleri ile State

State sadece sayılar için değil, her türlü JavaScript veri tipi için kullanılabilir:

function UserProfile() {

  // String state

  const [name, setName] = useState('');

  

  // Boolean state

  const [isLoggedIn, setIsLoggedIn] = useState(false);

  

  // Array state

  const [hobbies, setHobbies] = useState([]);

  

  // Object state

  const [user, setUser] = useState({

    name: '',

    email: '',

    age: 0

  });

  

  return (

    <div>

      <h2>Kullanıcı Profili</h2>

      <p>İsim: {name}</p>

      <p>Giriş Durumu: {isLoggedIn ? 'Giriş Yapılmış' : 'Giriş Yapılmamış'}</p>

      <p>Hobiler: {hobbies.join(', ')}</p>

      <p>Kullanıcı: {user.name} - {user.email}</p>

    </div>

  );

}

State Güncelleme Kuralları

State'i güncellerken dikkat edilmesi gereken önemli kurallar vardır:

1. State'i Doğrudan Değiştirmeyin:

// Yanlış - State'i doğrudan değiştirme

function WrongExample() {

  const [user, setUser] = useState({ name: 'Ahmet', age: 25 });

  

  const updateAge = () => {

    user.age = 26; // Bu yanlış!

    setUser(user); // Bu da çalışmayacak

  };

  

  return <button onClick={updateAge}>Yaşı Güncelle</button>;

}


// Doğru - Yeni obje oluşturma

function CorrectExample() {

  const [user, setUser] = useState({ name: 'Ahmet', age: 25 });

  

  const updateAge = () => {

    setUser({ ...user, age: 26 }); // Spread operator ile yeni obje

  };

  

  return <button onClick={updateAge}>Yaşı Güncelle</button>;

}

2. Array State Güncelleme:

function TodoList() {

  const [todos, setTodos] = useState([]);

  

  const addTodo = (text) => {

    // Yeni todo ekleme

    setTodos([...todos, { id: Date.now(), text, completed: false }]);

  };

  

  const removeTodo = (id) => {

    // Todo silme

    setTodos(todos.filter(todo => todo.id !== id));

  };

  

  const toggleTodo = (id) => {

    // Todo durumunu değiştirme

    setTodos(todos.map(todo => 

      todo.id === id ? { ...todo, completed: !todo.completed } : todo

    ));

  };

  

  return (

    <div>

      {todos.map(todo => (

        <div key={todo.id}>

          <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>

            {todo.text}

          </span>

          <button onClick={() => toggleTodo(todo.id)}>Tamamla</button>

          <button onClick={() => removeTodo(todo.id)}>Sil</button>

        </div>

      ))}

      <button onClick={() => addTodo('Yeni görev')}>Görev Ekle</button>

    </div>

  );

}

Props Nedir? Neden Kullanılır?

Props (properties), React bileşenlerine dışarıdan veri aktarmak için kullanılan mekanizmadır. Props, bir bileşenin parametreleri gibi düşünülebilir. Üst bileşen (parent), alt bileşene (child) props aracılığıyla veri gönderebilir.

Props'un temel özellikleri:

Salt Okunur (Read-Only): Props, alan bileşen tarafından değiştirilemez. Bu, tek yönlü veri akışını sağlar ve uygulamanın öngörülebilirliğini artırır.

Dışarıdan Gelen Veri: Props, bileşenin dışından gelen verilerdir. Bu sayede aynı bileşen farklı verilerle yeniden kullanılabilir.

Herhangi Bir Veri Tipi: Props olarak string, number, boolean, array, object, function gibi herhangi bir JavaScript veri tipi gönderilebilir.

Props Kullanımı (Veri Aktarımı)

Temel Props Kullanımı

// Alt bileşen - props alır

function Greeting(props) {

  return <h1>Merhaba, {props.name}!</h1>;

}


// Üst bileşen - props gönderir

function App() {

  return (

    <div>

      <Greeting name="Ahmet" />

      <Greeting name="Fatma" />

      <Greeting name="Mehmet" />

    </div>

  );

}

Destructuring ile Props Kullanımı

Props'u daha temiz bir şekilde kullanmak için destructuring yapılabilir:

// Destructuring ile props alma

function UserCard({ name, email, avatar, isOnline }) {

  return (

    <div className="user-card">

      <img src={avatar} alt={`${name} avatar`} />

      <h3>{name}</h3>

      <p>{email}</p>

      <span className={`status ${isOnline ? 'online' : 'offline'}`}>

        {isOnline ? 'Çevrimiçi' : 'Çevrimdışı'}

      </span>

    </div>

  );

}


// Kullanımı

function UserList() {

  return (

    <div>

      <UserCard 

        name="Ahmet Yılmaz"

        email="ahmet@example.com"

        avatar="avatar1.jpg"

        isOnline={true}

      />

      <UserCard 

        name="Fatma Kaya"

        email="fatma@example.com"

        avatar="avatar2.jpg"

        isOnline={false}

      />

    </div>

  );

}

Default Props

Bileşenlere varsayılan props değerleri verilebilir:

function Button({ text, color, size, onClick }) {

  return (

    <button 

      className={`btn btn-${color} btn-${size}`}

      onClick={onClick}

    >

      {text}

    </button>

  );

}


// Default props tanımlama

Button.defaultProps = {

  text: 'Tıkla',

  color: 'primary',

  size: 'medium',

  onClick: () => {}

};


// Modern yaklaşım - destructuring ile default değerler

function Button({ 

  text = 'Tıkla', 

  color = 'primary', 

  size = 'medium', 

  onClick = () => {} 

}) {

  return (

    <button 

      className={`btn btn-${color} btn-${size}`}

      onClick={onClick}

    >

      {text}

    </button>

  );

}

Fonksiyon Props

Props olarak fonksiyonlar da gönderilebilir. Bu, alt bileşenin üst bileşenle iletişim kurmasını sağlar:

function SearchBox({ onSearch, placeholder }) {

  const [query, setQuery] = useState('');

  

  const handleSubmit = (e) => {

    e.preventDefault();

    onSearch(query); // Üst bileşene veri gönderme

  };

  

  return (

    <form onSubmit={handleSubmit}>

      <input 

        type="text"

        value={query}

        onChange={(e) => setQuery(e.target.value)}

        placeholder={placeholder}

      />

      <button type="submit">Ara</button>

    </form>

  );

}


function App() {

  const [searchResults, setSearchResults] = useState([]);

  

  const handleSearch = (query) => {

    // Arama işlemi yapılır

    console.log('Aranan:', query);

    // API çağrısı yapılabilir

    // setSearchResults(results);

  };

  

  return (

    <div>

      <SearchBox 

        onSearch={handleSearch}

        placeholder="Ne aramak istiyorsunuz?"

      />

      {/* Arama sonuçları gösterimi */}

    </div>

  );

}

Tek Yönlü Veri Akışı

React'te veri akışı tek yönlüdür: üst bileşenden alt bileşene doğru. Bu, uygulamanın veri yönetimini daha öngörülebilir ve hata ayıklamasını daha kolay hale getirir.

Veri Akışı Prensibi

function App() {

  const [users, setUsers] = useState([

    { id: 1, name: 'Ahmet', email: 'ahmet@example.com' },

    { id: 2, name: 'Fatma', email: 'fatma@example.com' }

  ]);

  

  const addUser = (newUser) => {

    setUsers([...users, { ...newUser, id: Date.now() }]);

  };

  

  const deleteUser = (userId) => {

    setUsers(users.filter(user => user.id !== userId));

  };

  

  return (

    <div>

      <UserForm onAddUser={addUser} />

      <UserList users={users} onDeleteUser={deleteUser} />

    </div>

  );

}


function UserList({ users, onDeleteUser }) {

  return (

    <div>

      <h2>Kullanıcı Listesi</h2>

      {users.map(user => (

        <UserItem 

          key={user.id}

          user={user}

          onDelete={() => onDeleteUser(user.id)}

        />

      ))}

    </div>

  );

}


function UserItem({ user, onDelete }) {

  return (

    <div className="user-item">

      <span>{user.name} - {user.email}</span>

      <button onClick={onDelete}>Sil</button>

    </div>

  );

}


function UserForm({ onAddUser }) {

  const [name, setName] = useState('');

  const [email, setEmail] = useState('');

  

  const handleSubmit = (e) => {

    e.preventDefault();

    if (name && email) {

      onAddUser({ name, email });

      setName('');

      setEmail('');

    }

  };

  

  return (

    <form onSubmit={handleSubmit}>

      <h2>Yeni Kullanıcı Ekle</h2>

      <input 

        type="text"

        value={name}

        onChange={(e) => setName(e.target.value)}

        placeholder="İsim"

      />

      <input 

        type="email"

        value={email}

        onChange={(e) => setEmail(e.target.value)}

        placeholder="E-posta"

      />

      <button type="submit">Ekle</button>

    </form>

  );

}

State Yukarı Taşıma (Lifting State Up)

Birden fazla bileşenin aynı state'i paylaşması gerektiğinde, state ortak üst bileşene taşınır:

function TemperatureCalculator() {

  const [temperature, setTemperature] = useState('');

  const [scale, setScale] = useState('c');

  

  const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;

  const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

  

  return (

    <div>

      <TemperatureInput

        scale="c"

        temperature={celsius}

        onTemperatureChange={(temp) => {

          setTemperature(temp);

          setScale('c');

        }}

      />

      <TemperatureInput

        scale="f"

        temperature={fahrenheit}

        onTemperatureChange={(temp) => {

          setTemperature(temp);

          setScale('f');

        }}

      />

      <BoilingVerdict celsius={parseFloat(celsius)} />

    </div>

  );

}


function TemperatureInput({ scale, temperature, onTemperatureChange }) {

  const scaleName = scale === 'c' ? 'Celsius' : 'Fahrenheit';

  

  return (

    <fieldset>

      <legend>{scaleName} cinsinden sıcaklık girin:</legend>

      <input

        value={temperature}

        onChange={(e) => onTemperatureChange(e.target.value)}

      />

    </fieldset>

  );

}


function BoilingVerdict({ celsius }) {

  if (celsius >= 100) {

    return <p>Su kaynar.</p>;

  }

  return <p>Su kaynamaz.</p>;

}


// Yardımcı fonksiyonlar

function toCelsius(fahrenheit) {

  return (fahrenheit - 32) * 5 / 9;

}


function toFahrenheit(celsius) {

  return (celsius * 9 / 5) + 32;

}


function tryConvert(temperature, convert) {

  const input = parseFloat(temperature);

  if (Number.isNaN(input)) {

    return '';

  }

  const output = convert(input);

  const rounded = Math.round(output * 1000) / 1000;

  return rounded.toString();

}

Bu örnekte, sıcaklık değeri ve ölçek bilgisi üst bileşende (TemperatureCalculator) tutulur ve alt bileşenlere props olarak geçirilir. Bu sayede her iki input alanı da senkronize kalır ve su kaynatma durumu doğru şekilde hesaplanır.

State ve props, React'in temel yapı taşlarıdır. State bileşenlerin dinamik verilerini yönetirken, props bileşenler arası veri aktarımını sağlar. Bu iki kavramı doğru şekilde kullanmak, etkili React uygulamaları geliştirmenin anahtarıdır.