Detaylı React Hooks Kullanımı: useState
Tags: react, usestate, hooks, webdev
Published: 2023-04-04 13:30:43

### `useState` hook'u nedir ve nasıl kullanılır?

`useState`, React ile birlikte gelen çok kullanışlı bir hook'tur. Ancak kullanımına geçmeden önce bu hook'un kullanımında dikkat edilmesi gereken birkaç noktaya (veya uyulması gereken kurallara) değinelim.

**1.** `useState` hook'u yalnızca `functional component`'ler ile kullanılır. Yani, `class component`'ler ile kullanmaya kalktığımızda hata alırız. Peki `functional component` nedir? `Create-react-app` kurduğumuzda gelen `App.js` dosyasını temizleyip aşağıdaki hale getirelim:

```js
function App (props) {
  return <h1>Hello, {props.name}</h1>;
}
```

Görüleceği üzere bir `functional component`, `props` ve `return` alan bir fonksiyondur. Yukarıdaki örneğin aynısını `class component` olarak yazmak istersek şöyle bir şey yazmamız gerekirdi:

```js
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
```

Biz bir `functional component`'in içerisine hook yazabilirken; `class component`'in içerisine yazamayız. Dolayısıyla dikkat edilmesi gereken ilk şey, `useState` hook'unu `functional component`'ler içerisinde kullanmaktır.

***

**2.** `useState` hook'unu bir `if` ifadesine veya `for` vb. gibi bir döngünün içerisine ekleyemeyiz. Yani, aşağıdaki gibi bir kullanım bize hata verecektir:

```js
import { useState } from "react";

function App() {

  if (true) {
    useState();
  }


  return (
    <div className="App">
      hello
    </div>
  );
}

export default App;
```

Alacağımız hata ise şöyle olacaktır:

```bash
React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
```

Sonuç olarak hook'ları,

- Bir `if` ifadesinin içerisinde kullanamayız,
- Bir `for` döngüsünün (genel olarak bir `loop`'un) içerisinde kullanamayız,
- Bir `fonksiyon`'un içerisinde kullanamayız.

Yani hook'lar herhangi bir ifade ile iç içe geçemez (*nested*). Bir `functional component`'in içerisinde yalnızca **top-level** olarak kullanılabilirler.

***

Tüm bu kurallardan sonra asıl soruya gelelim: **`useState` hook'u nedir ve nasıl kullanılır?** Öncelikle `useState` hook'unun sentaksını, yani biçimini/formunu gösterelim:

```js

import { useState } from "react";

function App() {

const [state, setState] = useState(initialState);

return (
    <div className="App">
      {state}
    </div>
  );
}

```

React `useState` hook'u, bir `functional component`'teki `state`'i / `durum`'u / `hâl`'i takip etmemize/izlememize olanak tanır. `state` genellikle bir uygulamada takip edilmesi/izlenmesi gereken verileri veya özellikleri ifade eder.

Yukarıdaki kodda ilk olarak `destructuring { ... }` kullanarak `react` içerisinden `useState`'i dosyamıza dahil ettik.

`useState` bir `initial state` alır ve iki değer döndürür:

- Şu anki `state`,
- `state`'i güncelleyen bir fonksiyon.

Dolayısıyla biz `useState` kullanırken bir `array` içerisinde bunları yazarız. `useState` hook'unun içerisine de o `state`'in `başlangıç hâli`'ni, yani `initial state`'i yazarız. Bu `initial state` herhangi bir değer olabilir: `string`, `number`, `array`, `object`, `boolean` vb. Meselâ şöyle bir kodumuz olsun:

```js

import { useState } from "react";

function FavoriteColor() {

  const [color, setColor] = useState("");

}

```

İlk değer olan `color`, mevcut `state`'imizdir.

İkinci değer olan `setColor`, `state`'imizi güncellemek için kullanılan fonksiyondur.

Son olarak, `initial state`'i boş bir `string` olarak ayarladık: `useState("")`. Dolayısıyla `color` adındaki `state`'in şu anki değeri boş bir `string` şeklindedir.

Bu `initial state`'i meselâ `useState("red")` şeklinde de ayarlayabilirdik. Bu durumda `color` adındaki `state`'in şu anki değeri `red` olurdu:

```js

import { useState } from "react";

function FavoriteColor() {
  const [color, setColor] = useState("red");

  return <h1>En sevdiğim renk {color}!</h1>
}

```

Ekranımızda şöyle bir şey görecektik: `En sevdiğim renk red!`

Peki `setColor` fonksiyonunu nasıl kullanabilir ve `color` adındaki `state`'in aldığı değeri değiştirebiliriz? Bir `button` ekleyelim ve bu `button`'a da bir `onClick` event'i ekleyelim:

```js

import { useState } from "react";

function FavoriteColor() {
  const [color, setColor] = useState("red");

  return (
    <>
      <h1>My favorite color is {color}!</h1>
      <button
        type="button"
        onClick={() => setColor("blue")}
      >Blue</button>
    </>
  )
}

```

İçerisinde `onClick` event'i olan bir `button` ekledik. Bu `button`'a tıkladığımızda `color` adındaki `state`'in değeri `blue` değerini alacaktır. Çünkü `onClick` event'i içerisinde `setColor` fonksiyonunu çağırdık ve `blue` değerini verdik. Yani aslında `setColor` fonksiyonuyla biz şunu demiş/yapmış oluyoruz: `color` adındaki `state`'in değerini `blue` olarak güncelle (*set* et).

Yukarıda da belirttiğimiz üzere, bir `useState` hook'u `string`'leri, `number`'ları, `boolean`'ları, `array`'leri, `object`'leri ve bunların herhangi bir kombinasyonunu takip etmek için kullanılabilir! Aşağıdaki örneğe bakalım:

```js

import { useState } from "react";

function Car() {
  const [brand, setBrand] = useState("Ford");
  const [model, setModel] = useState("Mustang");
  const [year, setYear] = useState("1964");
  const [color, setColor] = useState("red");

  return (
    <>
      <h1>Şu arabaya sahibim: {brand}</h1>
      <p>
        Arabam {color} renginde ve modeli şu: {year} {model}.
      </p>
    </>
  )
}

```

Yukarıdaki örnekte `useState` hook'larını kullanarak `brand`, `model`, `year` ve `color` özelliklerini takip ediyoruz. Bu özelliklerin her biri bir `state`'dir. Bu `state`'lerin değerlerini de `setBrand`, `setModel`, `setYear` ve `setColor` fonksiyonlarıyla güncelleyebiliyoruz. Yukarıdaki kod bize şu çıktıyı verecektir:

```bash

Şu arabaya sahibim: Ford

Arabam red renginde ve modeli şu: 1964 Mustang.

```

Yukarıdaki gibi `multiple state`, yani birçok `state`'ten oluşan bir kod yazmak yerine aynı şeyi bir `object` ve bir `hook` kullanarak da yapabilirdik:

```js

import { useState } from "react";

function Car() {
  const [car, setCar] = useState({
    brand: "Ford",
    model: "Mustang",
    year: "1964",
    color: "red"
  });

  return (
    <>
      <h1>Şu arabaya sahibim: {car.brand}</h1>
      <p>
        Arabam {car.color} renginde ve modeli şu: {car.year} {car.model}.
      </p>
    </>
  )
}

```

Bu kodla da aynı çıktıyı almış olurduk.

***

### Bir `state` İçerisindeki `objects`'i veya `arrays`'i Güncellemek

Bir `state` güncellendiğinde, aslında, bu `state` tüm `state`'in  üzerine yazılır.

Peki ya sadece arabamızın rengini güncellemek istiyorsak?

Sadece setCar({color: "blue"}) yazarsak, bu `setCar` fonksiyonu `brand`, `model` ve `year`'ı halihazırda mevcut olan `state`'imizden kaldıracaktır. Bir örnek kod üzerinden ne olduğunu daha iyi göstermiş olalım:

```js

import { useState } from "react";

function App() {
  const [car, setCar] = useState({
    brand: "Ford",
    model: "Mustang",
    year: "1964",
    color: "red"
  });

  const updateColor = () => {
    setCar( {color: "blue" });
  }

  return (
    <>
      <h1>My {car.brand}</h1>
      <p>
        It is a {car.color} {car.model} from {car.year}.
      </p>
      <button
        type="button"
        onClick={updateColor}
      >Blue</button>
    </>
  )
}

export default App;

```

Yukarıdaki kodu çalıştırdığımızda şu çıktıyı alacağız:

```bash

My Ford

It is a red Mustang from 1964.

Blue (button)

```

Ekrandaki `Blue` button'ına tıkladığımızda, çıktımız şu hâli alacaktır:

```bash

My
It is a blue from .

Blue (button)

```

Görüleceği üzere, `setCar` fonksiyonu, `brand`, `model` ve `year`'ı kaldırmıştır. Yani, sadece `color` özelliğini güncellemek istedik ancak `setCar` fonksiyonu, tüm `state`'in üzerine yalnızca `color` özelliğini yazdı ve diğer tüm özellikleri kaldırdı. Bunun olmasını istemeyiz. Bu yüzden `brand`, `model` ve `year`'ı tekrar eklememiz gerekiyor. Bu işlemi yapmak için JavaScript'teki `spread` operatörünü kullanabiliriz:

```js

import { useState } from "react";

function App() {
  const [car, setCar] = useState({
    brand: "Ford",
    model: "Mustang",
    year: "1964",
    color: "red"
  });

  const updateColor = () => {
    setCar(previousState => {
      return { ...previousState, color: "blue" }
    });
  }

  return (
    <>
      <h1>My {car.brand}</h1>
      <p>
        It is a {car.color} {car.model} from {car.year}.
      </p>
      <button
        type="button"
        onClick={updateColor}
      >Blue</button>
    </>
  )
}

export default App;


```

Buradaki dikkat etmemiz gereken `updateColor` şeklindeki fonksiyon içerisinde çağırdımız `setCar` hook'unun aslında `spread` operatörüyle birlikte bir `callback` fonksiyon aldığı. Bu örnekte, `setCar` hook'u içindeki `previousState` isimli bir `callback` fonksiyon kullanılmaktadır. Bu fonksiyon, `setCar` tarafından geçerli olan önceki `state`'i temsil eder ve yeni bir `state` döndürmek için kullanılır. `updateColor` fonksiyonu, `setCar` fonksiyonunu çağırdığında, mevcut `state`'i almak için bu `callback` fonksiyonu kullanır ve mevcut `state` ile birlikte, yani mevcut `state`'i muhafaza ederek yeni bir `state` döndürür.

Özetlemek gerekirse: `setCar` fonksiyonuna bir `callback` fonksiyonu (yani, `previousState`) geçirilir. Bu `callback` fonksiyonu, mevcut `state`'i alır ve bu `state`'in bir kopyasını oluşturarak `color` özelliğini `blue` olarak değiştirir. Bu yeni `state`, `setCar` fonksiyonu tarafından aktif hale getirilir ve component yeniden render edilir.

Dolayısıyla biz ekrandaki `Blue` butonuna tıkladığımızda bir önceki bilgiler, yani `brand`, `model` ve `year` kaybolmamış olur. Çünkü `setCar` fonksiyonu, mevcut `state`'i muhafaza ederek ve yalnızca `color` özelliğini güncelleyerek yeni bir `state` döndürür.

`useState` hook'unun `class component`'ler ile karşılaştırmalı ve daha detaylı bir şekilde anlatıldığı şu yazıyı okumanızı tavsiye ederim:

https://blog.webdevsimplified.com/2020-04/use-state/