State Güncellemek

Çoğu zaman, birden çok componentin aynı state’i yansıtması gerekir. Bu bölümde, suyun belirli bir sıcaklıkta kaynayıp kaynayamayacağını hesaplayan fonksiyonları oluşturacağız.

BoilingVerdict adlı bir componentle başlayacağız. Celsius sıcaklığını bir props ile parametre olarak kabul eder ve suyu kaynatmaya yetecek kadar olup olmadığını return eder:

function BoilingVerdict(props) {
  if (props.celsius >= 100) {
    return <p>Su kaynar.</p>;
  }
  return <p>Suyun kaynaması için yeterli sıcaklık değil.</p>;
}

Sonra, Calculator adlı bir component oluşturacağız.

Sıcaklığı girmenizi sağlayıp, bu değeri this.state.temperature‘te tutacak:

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    return (
      <fieldset>
        <legend>Celsius sıcaklığını giriniz:</legend>
        <input value={temperature} onChange={this.handleChange} />
        <BoilingVerdict celsius={parseFloat(temperature)} />
      </fieldset>
    );
  }
}

CodePen’de Deneyin

İkinci Inputu Eklemek

Şimdi yapacağımız şey ise Celsius inputuna ek olarak, bir Fahrenheit inputu sağlamak.

TemperatureInput componentinden devam edebiliriz. Yeni bir scale propsu ekleyeceğiz ki bunlar c ya da f olabilirler:

const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};

class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>{scaleNames[scale]} sıcaklığını giriniz:</legend>
        <input value={temperature} onChange={this.handleChange} />
      </fieldset>
    );
  }
}

Artık iki ayrı sıcaklık girişi oluşturmak için Calculator componentini şu şekilde değiştirebiliriz:

class Calculator extends React.Component {
  render() {
    return (
      <div>
        <TemperatureInput scale="c" />
        <TemperatureInput scale="f" />
      </div>
    );
  }
}

CodePen’de Deneyin

Şu anda iki inputumuz var, ancak sıcaklıkların birine rakam yazdığımızda diğeri güncellenmiyor. Bu bizim gereksinimimizle çelişiyor, onları senkronize etmeyi istiyoruz.

Dönüştürme Fonksiyonları

İlk olarak Celsius ve Fahrenheit’ı birbirine dönüştürmek için gerekli iki fonksiyonu yazacağız:

function toCelsius(fahrenheit) {
  return (fahrenheit - 32) * 5 / 9;
}

function toFahrenheit(celsius) {
  return (celsius * 9 / 5) + 32;
}

Bu iki fonksiyon, sıcaklıkları birbirine dönüştürür.

State Güncellemek

Şu anda, TemperatureInput componenti bağımsız olarak değerlerini local state’te tutuyor:

class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    // ...   

Bununla birlikte, bu iki inputun birbiriyle senkronize edilmesini istiyoruz. Celsius inputu güncellendiğinde, Fahrenheit inputu dönüştürülen sıcaklığı göstermelidir; aynı zamanda tersi de çalışmalıdır.

React’ta state, ihtiyaç duyan componentlerin en yakın ortak atasına taşınarak gerçekleştirilir.

Bunun yerine, state’i TemperatureInputden çıkaracak ve onu Calculator içine taşıyacağız.

Bunun nasıl olduğunu adım adım işleyelim.

İlk olarak, TemperatureInput componentinde this.state.temperature öğesini this.props.temperature olarak değiştirelim.

  render() {
    // Önceden: const temperature = this.state.temperature;
    const temperature = this.props.temperature;
    // ...

Propsların read-only (sadece okunabilir, değiştirilemez) olduğunu biliyoruz. temperature state’teyken, TemperatureInput bunu değiştirmek için this.setState()yi çağırabilir.

Şimdi, TemperatureInput sıcaklığı güncellemek istediğinde, this.props.onTemperatureChange fonksiyonu yardımı ile yapacak:

  handleChange(e) {
    // Önceden: this.setState({temperature: e.target.value});
    this.props.onTemperatureChange(e.target.value);
    // ...

Not:

temperature veya onTemperatureChange adlarının özel bir anlamı yoktur. Onlara, value ve onChange gibi isimler verdik, başka bir şey diyebilirdik.

class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.onTemperatureChange(e.target.value);
  }

  render() {
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>{scaleNames[scale]} sıcaklığını giriniz:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

Şimdi Calculator componentine geçelim. Mevcut inputun temperature ve scale değerlerini statede tutarız.

Örneğin, Celsius’a 37 yazarsak, Calculator componentinin state’i şöyle olacaktır:

{
  temperature: '37',
  scale: 'c'
}

Eğer daha sonra Fahrenheit’ı 212 olarak değişirsek Calculator componenti şöyle olur:

{
  temperature: '212',
  scale: 'f'
}

Her ikisinin girdisinide tutabilirdik fakat gereksiz olurdu. En son girilen girdinin değerini ve gösterdiği ölçeği tutmak yeterlidir. Daha sonra temperature ve scale değerlerine bağlı olarak diğerinin değerini hesaplayabiliriz.

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
  }

  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />

        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />

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

CodePen’de Deneyin

Şu anda, hangi inputun düzenlediğiniz önemli değil, Calculator’daki this.state.temperature ve this.state.scale güncellenir. Inputlardan bir tanesi olduğu gibi değeri alır, diğer input değeri daima buna dayalı olarak yeniden hesaplanır.

Monitoring State in React DevTools

Sıradaki Eğitim: Composition ve Inheritance