Reacktif Efektlerin Yaşam Döngüsü

Efektler bileşenlerden farklı bir yaşam döngüsü vardır. Bileşenler takılabilir, güncellenebilir veya çıkarılabilir.Efektler sadece iki şey yapabilir: bir şeyi senkronize etmeye başlamak için, ve daha sonra senkronizasyonu durdurmak için. Efektler zaman içinde değişen sahne ve durumlara bağlıysa bu döngü birden çok kez gerçekleşebilir. React, Efekt’inizin bağımlılıklarını doğru belirtip belirtmediğinizi kontrol etmek için bir linter kuralı sağlar. Bu, Efektinizin en son props ve state ile senkronize olmasını sağlar.

Bunları öğreneceksiniz

Efektlerin yaşam döngüsü bir bileşenin yaşam döngüsünden nasıl farklıdır

  • Her bir Efekt tek başına nasıl düşünülebilir
  • Efekt ne zaman ve neden yeniden senkronize edilmesi gerektiği
  • Efekt bağımlılıkları nasıl belirlenir?
  • Bir değerin reaktif olması ne anlama gelir
  • Boş bir bağımlılık dizisi ne anlama gelir?
  • React, bir linter ile bağımlılıklarınızın doğru olduğunu nasıl doğrular
  • Linter ile aynı fikirde olmadığınızda ne yapmalısınız

Efektin Yaşam Döngüsü

Her React bileşeni aynı yaşam döngüsünden geçer:

  • Bir bileşen ekrana eklendiğinde monte edilir.
  • Bir bileşen, genellikle bir etkileşime yanıt olarak yeni prop’lar veya state aldığında updates yapar.
  • Bir bileşen ekrandan kaldırıldığında unmounts olur.

Bileşenler hakkında düşünmek için iyi bir yol, ancak Efektler hakkında değildir. Bunun yerine, her bir Efekt bileşeninizin yaşam döngüsünden bağımsız olarak düşünmeye çalışın. Bir Efekt harici bir sistemin mevcut prop’lara ve state nasıl senkronize edileceğini açıklar. Kodunuz değiştikçe, senkronizasyonun daha sık veya daha seyrek yapılması gerekecektir.

Bu noktayı açıklamak için, bileşeninizi bir sohbet sunucusuna bağlayan bu Efekti düşünün:

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);
// ...
}

Efektinizin gövdesi senkronizasyonun nasıl başlatılacağını belirtir:

// ...
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
// ...

Efektiniz tarafından döndürülen temizleme işlevi senkronizasyonun nasıl durdurulacağını belirtir:

// ...
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
// ...

Sezgisel olarak, React’in bileşeniniz bağlandığında senkronizasyonu başlatacağını ve bileşeniniz ayrıldığında senkronizasyonu durduracağını düşünebilirsiniz. Ancak, bu hikayenin sonu değildir! Bazen, bileşen takılı kalırken senkronizasyonu birden çok kez başlatmak ve durdurmak da gerekebilir.

Şimdi bunun neden gerekli olduğuna, ne zaman gerçekleştiğine ve _bu davranışı nasıl kontrol edebileceğinize bakalım.

Not

Bazı Efektler hiç temizleme fonksiyonu döndürmez. Çoğu zaman, bir tane döndürmek isteyeceksiniz-ama döndürmezseniz, React boş bir temizleme fonksiyonu döndürmüşsünüz gibi davranacaktır.

Senkronizasyonun neden birden fazla kez yapılması gerekebilir

Bu ChatRoom bileşeninin, kullanıcının bir açılır menüden seçtiği bir roomId prop’larını aldığını düşünün. Diyelim ki kullanıcı başlangıçta roomId olarak "genel" odasını seçti. Uygulamanız "genel" sohbet odasını görüntüler:

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId /* "genel" */ }) {
// ...
return <h1>{roomId} odasına hoş geldiniz!</h1>;
}

UI görüntülendikten sonra, React senkronizasyonu başlatmak için Efektinizi çalıştıracaktır. "genel" odasına bağlanır:

function ChatRoom({ roomId /* "genel" */ }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId); // "genel" odaya bağlanır
connection.connect();
return () => {
connection.disconnect(); // "genel" oda ile bağlantıyı keser
};
}, [roomId]);
// ...

Buraya kadar her şey yolunda.

Daha sonra, kullanıcı açılır menüden farklı bir oda seçer (örneğin, "seyahat"). İlk olarak, React kullanıcı arayüzünü güncelleyecektir:

function ChatRoom({ roomId /* "seyahat" */ }) {
// ...
return <h1>{roomId} odasına hoş geldiniz!</h1>;
}

Bundan sonra ne olması gerektiğini düşünün. Kullanıcı, kullanıcı arayüzünde seçili sohbet odasının "seyahat" olduğunu görür. Ancak, son kez çalışan Efekt hala "genel" odasına bağlı. roomId prop’u değişti, bu nedenle Efektinizin o zaman yaptığı şey ("genel" odasına bağlanmak) artık kullanıcı arayüzüyle eşleşmiyor.

Bu noktada, React’in iki şey yapmasını istersiniz:

  1. Eski roomId ile senkronizasyonu durdurun ("genel" oda ile bağlantıyı kesin)
  2. Yeni roomId ile senkronizasyonu başlatın ("seyahat" odasına bağlanın)

Neyse ki, React’e bunların her ikisini de nasıl yapacağını zaten öğrettiniz. Efektinizin gövdesi senkronizasyonun nasıl başlatılacağını ve temizleme fonksiyonunuz da senkronizasyonun nasıl durdurulacağını belirtir. React’in şimdi yapması gereken tek şey, bunları doğru sırada ve doğru prop ve state ile çağırmaktır. Bunun tam olarak nasıl gerçekleştiğini görelim.

React Efektinizi Nasıl Yeniden Senkronize Eder?

Hatırlayın, ChatRoom bileşeniniz roomId özelliği için yeni bir değer aldı. Eskiden "genel" idi ve şimdi "seyahat" oldu. React’in sizi farklı bir odaya yeniden bağlamak için Efektinizi yeniden senkronize etmesi gerekiyor.

React, senkronizasyonu durdurmak için, Efektinizin "genel" odasına bağlandıktan sonra döndürdüğü temizleme fonksiyonunu çağıracaktır. roomId ”genel”olduğu için, temizleme fonksiyonu”genel”` odasıyla bağlantıyı keser:

function ChatRoom({ roomId /* "genel" */ }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId); // "genel" odaya bağlanır
connection.connect();
return () => {
connection.disconnect(); // "genel" oda ile bağlantıyı keser
};
// ...

Ardından React, bu render sırasında sağladığınız Efekti çalıştıracaktır. Bu sefer, roomId "seyahat" olduğundan, "seyahat" sohbet odasına senkronize olmaya başlayacaktır (sonunda temizleme fonksiyonu da çağrılana kadar):

function ChatRoom({ roomId /* "seyahat" */ }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId); // "seyahat" odasına bağlanır
connection.connect();
// ...

Bu sayede, artık kullanıcının kullanıcı arayüzünde seçtiği odaya bağlanmış olursunuz. Felaket önlendi!

Bileşeniniz farklı bir roomId ile yeniden oluşturulduktan sonra her seferinde Efektiniz yeniden senkronize olacaktır. Örneğin, kullanıcı roomIdyi "seyahat"ten "müzik"e değiştirdi diyelim. React, temizleme fonksiyonunu çağırarak (sizi "seyahat" odasından ayırarak) Efektinizin senkronizasyonunu tekrar durdurur. Ardından, gövdesini yeni roomId prop ile çalıştırarak (sizi "müzik" odasına bağlayarak) tekrar senkronize etmeye başlayacaktır.

Son olarak, kullanıcı farklı bir ekrana geçtiğinde, ChatRoom bağlantıyı kaldırır. Artık bağlı kalmaya hiç gerek yok. React, Efektinizi son bir kez senkronize etmeyi durdurur ve sizi "müzik" sohbet odasından ayırır.

Efektin bakış açısından düşünmek

Şimdi `ChatRoom’ bileşeninin bakış açısından olan her şeyi özetleyelim:

  1. ChatRoom roomId "genel" olarak ayarlanmış şekilde monte edildi
  2. ChatRoom, roomId değeri "seyahat" olarak ayarlanarak güncellendi
  3. ChatRoom, roomId değeri "müzik" olarak ayarlanarak güncellendi
  4. ChatRoom bağlanmamış

Bileşenin yaşam döngüsündeki bu noktaların her biri sırasında, Efektiniz farklı şeyler yaptı:

  1. Efektiniz "genel" odaya bağlandı
  2. Efektinizin "genel" oda ile bağlantısı kesildi ve "seyahat" odasına bağlandı
  3. Efektinizin "seyahat" odasıyla bağlantısı kesildi ve "müzik" odasına bağlandı
  4. Efektinizin "müzik" odasıyla bağlantısı kesildi

Şimdi olanları bir de Efektin kendi perspektifinden düşünelim:

useEffect(() => {
// Efektiniz roomId ile belirtilen odaya bağlandı...
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
// ...bağlantısı kesilene kadar
connection.disconnect();
};
}, [roomId]);

Bu kodun yapısı, olanları birbiriyle örtüşmeyen bir dizi zaman dilimi olarak görmeniz için size ilham verebilir:

  1. Efektiniz "genel" odaya bağlandı (bağlantısı kesilene kadar)
  2. Efektiniz "seyahat" odasına bağlı (bağlantısı kesilene kadar)
  3. Efektiniz "müzik" odasına bağlı (bağlantısı kesilene kadar)

Önceden, bileşenin bakış açısından düşünüyordunuz. Bileşenin perspektifinden baktığınızda, Efektleri “render işleminden sonra” veya “unmount işleminden önce” gibi belirli bir zamanda ateşlenen “geri aramalar” veya “yaşam döngüsü olayları” olarak düşünmek cazip geliyordu. Bu düşünce tarzı çok hızlı bir şekilde karmaşıklaşır, bu nedenle kaçınmak en iyisidir.

Bunun yerine, her zaman bir seferde tek bir başlatma/durdurma döngüsüne odaklanın. Bir bileşenin takılıyor, güncelleniyor ya da sökülüyor olması önemli olmamalıdır. Yapmanız gereken tek şey senkronizasyonun nasıl başlatılacağını ve nasıl durdurulacağını açıklamaktır. Bunu iyi yaparsanız, Efektiniz ihtiyaç duyulduğu kadar çok kez başlatılmaya ve durdurulmaya dayanıklı olacaktır.

Bu size, JSX oluşturan işleme mantığını yazarken bir bileşenin monte edilip edilmediğini veya güncellenip güncellenmediğini nasıl düşünmediğinizi hatırlatabilir. Siz ekranda ne olması gerektiğini tanımlarsınız ve React gerisini çözer

React, Efektinizin yeniden senkronize olabileceğini nasıl doğrular

İşte oynayabileceğiniz canlı bir örnek. ChatRoom bileşenini bağlamak için “Sohbeti aç” düğmesine basın:

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);
  return <h1>{roomId} odasına hoş geldiniz!</h1>;
}

export default function App() {
  const [roomId, setRoomId] = useState('genel');
  const [show, setShow] = useState(false);
  return (
    <>
      <label>
        Sohbet odasını seçin:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="genel">genel</option>
          <option value="seyahat">seyahat</option>
          <option value="müzik">müzik</option>
        </select>
      </label>
      <button onClick={() => setShow(!show)}>
        {show ? 'Sohbeti kapat' : 'Sohbeti aç'}
      </button>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId} />}
    </>
  );
}

Bileşen ilk kez bağlandığında üç günlük gördüğünüze dikkat edin:

  1. ✅ "Genel" odaya bağlanma https://localhost:1234... (sadece geliştirme)
  2. ❌ "Genel" oda ile bağlantı kesildi https://localhost:1234. (sadece geliştirme)
  3. ✅ Adresinden "genel" odasına bağlanıyor https://localhost:1234...

İlk iki günlük yalnızca geliştirmeye yöneliktir. Geliştirme aşamasında, React her bileşeni her zaman bir kez yeniden bağlar.

React, Efektinizin yeniden senkronize olup olamayacağını, onu geliştirme aşamasında bunu hemen yapmaya zorlayarak doğrular. Bu size kapı kilidinin çalışıp çalışmadığını kontrol etmek için bir kapıyı açıp fazladan bir kez kapatmayı hatırlatabilir. React, kontrol etmek için geliştirme sırasında Efektinizi fazladan bir kez başlatır ve durdurur temizlemeyi iyi uyguladığınızı

Efektinizin pratikte yeniden senkronize olmasının ana nedeni, kullandığı bazı verilerin değişmiş olmasıdır. Yukarıdaki sanal alanda, seçili sohbet odasını değiştirin. RoomId` değiştiğinde Efektinizin nasıl yeniden senkronize olduğuna dikkat edin.

Ancak, yeniden senkronizasyonun gerekli olduğu daha sıra dışı durumlar da vardır. Örneğin, sohbet açıkken yukarıdaki sanal alanda sunucuUrlyi düzenlemeyi deneyin. Kodda yaptığınız düzenlemelere yanıt olarak Efekt’in nasıl yeniden senkronize olduğuna dikkat edin. Gelecekte React, yeniden senkronizasyona dayanan daha fazla özellik ekleyebilir.

React, Efekti yeniden senkronize etmesi gerektiğini nasıl anlar

React’in roomId değiştikten sonra Efektinizin yeniden senkronize edilmesi gerektiğini nasıl bildiğini merak ediyor olabilirsiniz. Çünkü React’e kodunun roomId’ye bağlı olduğunu bağımlılıklar listesi: içine dahil ederek söylediniz.

function ChatRoom({ roomId }) { // roomId özelliği zaman içinde değişebilir
useEffect(() => {
const connection = createConnection(serverUrl, roomId); // Bu Efektte roomId'yi okur
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]); // Böylece React'e bu Efektin roomId'ye "bağlı" olduğunu söylersiniz
// ...

Şöyle çalışıyor:

  1. RoomId`nin bir prop olduğunu biliyordunuz, bu da zaman içinde değişebileceği anlamına gelir.
  2. Efektinizin roomIdyi okuduğunu biliyordunuz (bu nedenle mantığı daha sonra değişebilecek bir değere bağlıdır).
  3. Bu yüzden onu Efektinizin bağımlılığı olarak belirlediniz (böylece roomId değiştiğinde yeniden senkronize olur).

Bileşeniniz yeniden oluşturulduktan sonra React her seferinde geçtiğiniz bağımlılıklar dizisine bakacaktır. Dizideki değerlerden herhangi biri, önceki render sırasında geçtiğiniz aynı noktadaki değerden farklıysa, React Efektinizi yeniden senkronize edecektir.

Örneğin, ilk render sırasında ["genel"] değerini geçtiyseniz ve daha sonra bir sonraki render sırasında ["seyahat"] değerini geçtiyseniz, React "genel" ve "seyahat" değerlerini karşılaştıracaktır. Bunlar farklı değerlerdir (Object.is ile karşılaştırıldığında), bu nedenle React Efektinizi yeniden senkronize edecektir. Öte yandan, bileşeniniz yeniden render edilirse ancak roomId değişmediyse, Efektiniz aynı odaya bağlı kalacaktır.

Her Efekt ayrı bir senkronizasyon sürecini temsil eder

Yalnızca bu mantığın daha önce yazdığınız bir Efekt ile aynı anda çalışması gerektiği için Efektinize ilgisiz bir mantık eklemekten kaçının. Örneğin, kullanıcı odayı ziyaret ettiğinde bir analiz olayı göndermek istediğinizi varsayalım. Zaten roomIdye bağlı bir Efektiniz var, bu nedenle analitik çağrısını oraya eklemek isteyebilirsiniz:

function ChatRoom({ roomId }) {
useEffect(() => {
logVisit(roomId);
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);
// ...
}

Ancak daha sonra bu Efekte bağlantıyı yeniden kurması gereken başka bir bağımlılık eklediğinizi düşünün. Bu Efekt yeniden senkronize olursa, aynı oda için logVisit(roomId) çağrısı da yapacaktır, ki bunu istememiştiniz. Ziyaretin günlüğe kaydedilmesi bağlantıdan ayrı bir süreçtir. Bunları iki ayrı Efekt olarak yazın:

function ChatRoom({ roomId }) {
useEffect(() => {
logVisit(roomId);
}, [roomId]);

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
// ...
}, [roomId]);
// ...
}

Kodunuzdaki her bir Efekt ayrı ve bağımsız bir senkronizasyon sürecini temsil etmelidir.

Yukarıdaki örnekte, bir Efektin silinmesi diğer Efektin mantığını bozmayacaktır. Bu, farklı şeyleri senkronize ettiklerinin iyi bir göstergesidir ve bu nedenle onları ayırmak mantıklıdır. Öte yandan, uyumlu bir mantık parçasını ayrı Efektlere bölerseniz, kod “daha temiz” görünebilir ancak bakımı daha zor olacaktır. Bu nedenle, kodun daha temiz görünüp görünmediğini değil, süreçlerin aynı mı yoksa ayrı mı olduğunu düşünmelisiniz.

Efektler reaktif değerlere “tepki verir”

Efektiniz iki değişkeni (serverUrl ve roomId) okuyor, ancak bağımlılık olarak yalnızca roomId belirtmişsiniz:

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);
// ...
}

Neden serverUrl bir bağımlılık olmak zorunda değil?

Çünkü serverUrl yeniden oluşturma nedeniyle asla değişmez. Bileşen kaç kez yeniden oluşturulursa oluşturulsun ve nedeni ne olursa olsun her zaman aynıdır. SunucuUrl` asla değişmediğinden, bunu bir bağımlılık olarak belirtmek mantıklı olmaz. Sonuçta, bağımlılıklar yalnızca zaman içinde değiştiklerinde bir şey yaparlar!

Öte yandan, roomId yeniden oluşturmada farklı olabilir. Bileşen içinde bildirilen prop’lar, state ve diğer değerler reaktiftir çünkü render sırasında hesaplanırlar ve React veri akışına katılırlar.

Eğer serverUrl bir state değişkeni olsaydı, reaktif olurdu. Reaktif değerler bağımlılıklara dahil edilmelidir:

function ChatRoom({ roomId }) { // Prop'ların zaman içinde değişimi
const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // State zaman içinde değişebilir

useEffect(() => {
const connection = createConnection(serverUrl, roomId); // Efektin prop'ları ve state okur
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId, serverUrl]); // Böylece React'e bu Efektin proplara ve state "bağlı" olduğunu söylersiniz
// ...
}

Sunucu URL`sini bir bağımlılık olarak dahil ederek, Efektin değiştikten sonra yeniden senkronize olmasını sağlarsınız.

Seçili sohbet odasını değiştirmeyi deneyin veya bu sanal alanda sunucu URL’sini düzenleyin:

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]);

  return (
    <>
      <label>
        Sunucu URL'si:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>{roomId} odasına hoş geldiniz!</h1>
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('genel');
  return (
    <>
      <label>
        Sohbet odasını seçin:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="genel">genel</option>
          <option value="seyahat">seyahat</option>
          <option value="müzik">müzik</option>
        </select>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}

roomId veya serverUrl gibi reaktif bir değeri her değiştirdiğinizde, Efekt sohbet sunucusuna yeniden bağlanır.

Boş bağımlılıklara sahip bir Efekt ne anlama gelir

Hem serverUrl hem de roomId öğelerini bileşenin dışına taşırsanız ne olur?

const serverUrl = 'https://localhost:1234';
const roomId = 'genel';

function ChatRoom() {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, []); // ✅ Tüm bağımlılıklar beyan edildi
// ...
}

Artık Efektinizin kodu hiçbir reaktif değer kullanmadığından bağımlılıkları boş olabilir ([]).

Bileşenin bakış açısından düşünürsek, boş [] bağımlılık dizisi, bu Efektin sohbet odasına yalnızca bileşen bağlandığında bağlandığı ve yalnızca bileşen ayrıldığında bağlantıyı kestiği anlamına gelir. (React’in mantığınızı stres testi için geliştirme sırasında fazladan bir kez daha senkronize edeceğini unutmayın).

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

const serverUrl = 'https://localhost:1234';
const roomId = 'genel';

function ChatRoom() {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, []);
  return <h1>{roomId} odasına hoş geldiniz!</h1>;
}

export default function App() {
  const [show, setShow] = useState(false);
  return (
    <>
      <button onClick={() => setShow(!show)}>
        {show ? 'Sohbeti kapat' : 'Sohbeti aç'}
      </button>
      {show && <hr />}
      {show && <ChatRoom />}
    </>
  );
}

Ancak, Efektin bakış açısından düşünürseniz takma ve çıkarma hakkında düşünmenize hiç gerek yoktur. Önemli olan, Efektinizin senkronizasyonu başlatmak ve durdurmak için ne yaptığını belirtmiş olmanızdır. Bugün, hiçbir reaktif bağımlılığı yoktur. Ancak kullanıcının zaman içinde roomId veya serverUrl değerlerini değiştirmesini isterseniz (ve bunlar reaktif hale gelirse), Efektinizin kodu değişmeyecektir. Sadece bunları bağımlılıklara eklemeniz gerekecektir.

Bileşen gövdesinde bildirilen tüm değişkenler reaktiftir

Tek reaktif değerler prop’lar ve state değildir. Bunlardan hesapladığınız değerler de reaktiftir. Prop’lar veya state değişirse bileşeniniz yeniden render edilir ve bunlardan hesaplanan değerler de değişir. Bu nedenle, Efekt tarafından kullanılan bileşen gövdesindeki tüm değişkenler Efekt bağımlılık listesinde olmalıdır.

Diyelim ki kullanıcı açılır menüden bir sohbet sunucusu seçebiliyor, ancak ayarlardan varsayılan bir sunucu da yapılandırabiliyor. Ayarlar durumunu zaten bir context içine koyduğunuzu ve böylece ayarlari bu bağlamdan okuduğunuzu varsayalım. Şimdi serverUrlyi props ve varsayılan sunucudan seçilen sunucuya göre hesaplarsınız:

function ChatRoom({ roomId, selectedServerUrl }) { // roomId reaktiftir
const settings = useContext(SettingsContext); // ayarlar reaktiftir
const serverUrl = selectedServerUrl ?? settings.defaultServerUrl; // serverUrl reaktiftir
useEffect(() => {
const connection = createConnection(serverUrl, roomId); // Efektiniz roomId ve serverUrl değerlerini okur
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId, serverUrl]); // Bu yüzden bunlardan herhangi biri değiştiğinde yeniden senkronize edilmesi gerekir!
// ...
}

Bu örnekte, serverUrl bir prop veya state değişkeni değildir. Render sırasında hesapladığınız normal bir değişkendir. Ancak render sırasında hesaplanır, bu nedenle yeniden render nedeniyle değişebilir. Bu yüzden reaktiftir.

Bileşen içindeki tüm değerler (prop’lar, durum ve bileşeninizin gövdesindeki değişkenler dahil) reaktiftir. Herhangi bir reaktif değer yeniden işlendiğinde değişebilir, bu nedenle reaktif değerleri Effect’in bağımlılıkları olarak eklemeniz gerekir.

Başka bir deyişle, Efektler bileşen gövdesindeki tüm değerlere “tepki” verir.

Derinlemesine İnceleme

Global veya değiştirilebilir değerler bağımlılık olabilir mi?

Değiştirilebilir değerler (global değişkenler dahil) reaktif değildir.

location.pathname gibi değişken bir değer bağımlılık olamaz. Değişkendir, bu nedenle React render veri akışının tamamen dışında herhangi bir zamanda değişebilir. Değiştirilmesi bileşeninizin yeniden render edilmesini tetiklemez. Bu nedenle, bağımlılıklarda belirtmiş olsanız bile, React değiştiğinde Efekti yeniden senkronize edeceğini bilemez. Bu aynı zamanda React’in kurallarını da ihlal eder, çünkü render sırasında (bağımlılıkları hesapladığınız zaman) değişebilir verileri okumak purity of rendering. Bunun yerine, `useSyncExternalStore’ ile harici bir değişebilir değeri okumalı ve abone olmalısınız.

ref.current gibi değiştirilebilir bir değer veya ondan okuduğunuz şeyler de bir bağımlılık olamaz. useRef tarafından döndürülen ref nesnesinin kendisi bir bağımlılık olabilir, ancak current özelliği kasıtlı olarak değiştirilebilir. Yeniden oluşturmayı tetiklemeden bir şeyi takip etmenizi sağlar Ancak onu değiştirmek yeniden oluşturmayı tetiklemediğinden, reaktif bir değer değildir ve React, değiştiğinde Efektinizi yeniden çalıştırmayı bilmez.

Bu sayfada aşağıda öğreneceğiniz gibi, bir linter bu sorunları otomatik olarak kontrol edecektir.

React, her reaktif değeri bir bağımlılık olarak belirttiğinizi doğrular

Eğer linter’ınız React için yapılandırılmışsa, Effect’inizin kodu tarafından kullanılan her reaktif değerin bağımlılığı olarak bildirilip bildirilmediğini kontrol edecektir. Örneğin, bu bir lint hatasıdır çünkü hem roomId hem de serverUrl reaktiftir:

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) { // roomId reaktiftir
  const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl reaktiftir

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  }, []); // <-- Burada bir sorun var!

  return (
    <>
      <label>
        Sunucu URL'si:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>{roomId} odasına hoş geldiniz!</h1>
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('genel');
  return (
    <>
      <label>
        Sohbet odasını seçin:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="genel">genel</option>
          <option value="seyahat">seyahat</option>
          <option value="müzik">müzik</option>
        </select>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}

Bu bir React hatası gibi görünebilir, ancak aslında React kodunuzdaki bir hataya işaret ediyor. Hem roomId hem de serverUrl zaman içinde değişebilir, ancak bunlar değiştiğinde Efektinizi yeniden senkronize etmeyi unutuyorsunuz. Kullanıcı kullanıcı arayüzünde farklı değerler seçtikten sonra bile ilk roomId ve serverUrl değerlerine bağlı kalacaksınız.

Hatayı düzeltmek için, roomId ve serverUrl değerlerini Efektinizin bağımlılıkları olarak belirtmek için linter’ın önerisini izleyin:

function ChatRoom({ roomId }) { // roomId reaktiftir
const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl reaktiftir
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]); // ✅ Bildirilen tüm bağımlılıklar
// ...
}

Bu düzeltmeyi yukarıdaki sandbox’ta deneyin. Linter hatasının ortadan kalktığını ve sohbetin gerektiğinde yeniden bağlandığını doğrulayın.

Not

Bazı durumlarda React, bir değerin bileşen içinde bildirilmiş olmasına rağmen asla değişmediğini bilmektedir. Örneğin, useState’den döndürülen set fonksiyonu ve useRef tarafından döndürülen ref nesnesi stable’dır - yeniden oluşturmada değişmeyecekleri garanti edilir. Stabil değerler reaktif değildir, bu yüzden onları listeden çıkarabilirsiniz. Bunları dahil etmeye izin verilir: değişmeyeceklerdir, bu yüzden önemli değildir.

Yeniden senkronize etmek istemediğinizde ne yapmalısınız?

Önceki örnekte, roomId ve serverUrl değerlerini bağımlılık olarak listeleyerek lint hatasını düzelttiniz.

*Bununla birlikte, bunun yerine bu değerlerin reaktif değerler olmadığını, yani yeniden oluşturma sonucunda değişemeyeceklerini linter’a “kanıtlayabilirsiniz”. Örneğin, serverUrl ve roomId render işlemine bağlı değilse ve her zaman aynı değerlere sahipse, bunları bileşenin dışına taşıyabilirsiniz. Artık bağımlılık olmalarına gerek yoktur:

const serverUrl = 'https://localhost:1234'; // serverUrl reaktif değil
const roomId = 'genel'; // roomId reaktif değil

function ChatRoom() {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, []); // ✅ Bildirilen tüm bağımlılıklar
// ...
}

Bunları Efektin içinde de taşıyabilirsiniz.* Render sırasında hesaplanmazlar, bu nedenle reaktif değildirler:

function ChatRoom() {
useEffect(() => {
const serverUrl = 'https://localhost:1234'; // serverUrl reaktif değil
const roomId = 'genel'; // roomId reaktif değil
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, []); // ✅ Bildirilen tüm bağımlılıklar
// ...
}

Efektler reaktif kod bloklarıdır. İçlerinde okuduğunuz değerler değiştiğinde yeniden senkronize olurlar. Etkileşim başına yalnızca bir kez çalışan olay işleyicilerin aksine, Efektler senkronizasyon gerektiğinde çalışır.

Bağımlılıklarınızı “seçemezsiniz. ” Bağımlılıklarınız, Efektte okuduğunuz her reaktif değeri içermelidir. Linter bunu zorunlu kılar. Bazen bu, sonsuz döngüler ve Efektinizin çok sık yeniden senkronize edilmesi gibi sorunlara yol açabilir. Bu sorunları linter’ı bastırarak çözmeyin! İşte bunun yerine deneyeceğiniz şey:

  • Efektinizin bağımsız bir senkronizasyon sürecini temsil edip etmediğini kontrol edin. Efektiniz hiçbir şeyi senkronize etmiyorsa, gereksiz olabilir. Birden fazla bağımsız şeyi senkronize ediyorsa, bölün.

  • Eğer prop’ların veya state’in en son değerini ona “tepki vermeden” ve Efekti yeniden senkronize etmeden okumak istiyorsanız, Efektinizi reaktif bir parçaya (Efekt içinde tutacağınız) ve reaktif olmayan bir parçaya (Efekt Olayı_ adı verilen bir şeye çıkaracağınız) bölebilirsiniz. Olayları Efektlerden ayırma hakkında bilgi edinin

  • Nesnelere ve işlevlere bağımlılık olarak güvenmekten kaçının Render sırasında nesneler ve işlevler oluşturur ve ardından bunları bir Efektten okursanız, her renderda farklı olurlar. Bu, Efektinizin her seferinde yeniden senkronize olmasına neden olur. Efektlerden gereksiz bağımlılıkları kaldırma hakkında daha fazla bilgi edinin

Tuzak

Linter sizin dostunuzdur, ancak yetkileri sınırlıdır. Linter yalnızca bağımlılıkların ne zaman yanlış olduğunu bilir. Her bir durumu çözmenin en iyi yolunu bilmez. Linter bir bağımlılık öneriyorsa, ancak bunu eklemek bir döngüye neden oluyorsa, bu linter’ın göz ardı edilmesi gerektiği anlamına gelmez. Efektin içindeki (veya dışındaki) kodu değiştirmeniz gerekir, böylece bu değer reaktif olmaz ve bir bağımlılık olmasına ihtiyaç kalmaz.

Mevcut bir kod tabanınız varsa, bu şekilde linter’ı bastıran bazı Efektleriniz olabilir:

useEffect(() => {
// ...
// 🔴 Linteri bu şekilde bastırmaktan kaçının:
// eslint-ignore-next-line react-hooks/exhaustive-deps
}, []);

Sonraki sayfalarda, bu kodu kuralları bozmadan nasıl düzelteceğinizi öğreneceksiniz. Her zaman düzeltmeye değer!

Özet

  • Bileşenler takılabilir, güncellenebilir ve çıkarılabilir.
  • Her Efektin çevresindeki bileşenden ayrı bir yaşam döngüsü vardır.
  • Her bir Efekt, başlatılabilen ve durdurulabilen ayrı bir senkronizasyon sürecini tanımlar.
  • Efektleri yazarken ve okurken, bileşenin bakış açısından (nasıl bağlandığı, güncellendiği veya kaldırıldığı) ziyade her bir Efektin bakış açısından (senkronizasyonun nasıl başlatılacağı ve durdurulacağı) düşünün.
  • Bileşen gövdesi içinde bildirilen değerler “reaktiftir”.
  • Reaktif değerler zaman içinde değişebileceğinden Efekti yeniden senkronize etmelidir.
  • Linter, Efekt içinde kullanılan tüm reaktif değerlerin bağımlılık olarak belirtildiğini doğrular.
  • Linter tarafından işaretlenen tüm hatalar meşrudur. Kuralları ihlal etmemek için kodu düzeltmenin her zaman bir yolu vardır.

Problem 1 / 5:
Her tuş vuruşunda yeniden bağlanmayı düzeltme

Bu örnekte, ChatRoom bileşeni, bileşen bağlandığında sohbet odasına bağlanır, bağlantıyı kestiğinde bağlantıyı keser ve farklı bir sohbet odası seçtiğinizde yeniden bağlanır. Bu davranış doğrudur, bu nedenle çalışmaya devam etmesi gerekir.

Ancak, bir sorun var. Alttaki mesaj kutusu girişine her yazdığınızda, ChatRoom ayrıca sohbete yeniden bağlanır. (Bunu konsolu temizleyerek ve girdiye yazarak fark edebilirsiniz.) Bunun gerçekleşmemesi için sorunu düzeltin.

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => connection.disconnect();
  });

  return (
    <>
      <h1>{roomId} odasına hoş geldiniz!</h1>
      <input
        value={message}
        onChange={e => setMessage(e.target.value)}
      />
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('genel');
  return (
    <>
      <label>
        Sohbet odasını seçin:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="genel">genel</option>
          <option value="seyahat">seyahat</option>
          <option value="müzik">müzik</option>
        </select>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}