React Native MQTT, mobil uygulamanıza bir API'yi sürekli sorgulamadan canlı cihaz verisi sağlar. Bu eğitimde bir React Native (veya Expo) uygulamasını MQTT.js ile güvenli WebSocket üzerinden bir bulut broker'a bağlayacak, bunu yeniden kullanılabilir bir hook içine saracak, bir cihaza komut gönderecek ve ilk denemelerin çoğunu bozan arka plan ile ön plan geçişlerini yöneteceksiniz.
React Native MQTT için Neden WebSocket?
React Native'in JavaScript çalışma ortamında ham TCP soketleri yoktur. Bunları yerel bir modülle ekleyebilirsiniz, ancak bu yerel derleme adımları, Expo dev client'ları ve platforma özgü hatalar demektir. React Native ise standartlara uygun bir WebSocket ile gelir ve MQTT.js, MQTT'yi bunun üzerinde çalıştırmayı zaten bilir. Sonuç, Expo Go'da, Android'de ve iOS'ta hiç yerel kod olmadan çalışan saf JavaScript bir client'tır.
ConnectMQ, herkesçe güvenilen bir sertifikayla 8084 portunda /mqtt yoluyla güvenli WebSocket sunar; dolayısıyla ihtiyacınız olan URL şudur:
wss://broker.connectmq.com:8084/mqtt
8084 portu HTTPS'e benzediği için çoğu kurumsal ağdan ve mobil operatör ağından da geçer. Tüm arka plan bilgisi için WebSocket üzerinden MQTT yazısına bakın.
Proje Kurulumu
Bir proje oluşturun, MQTT.js'i ve bir URL polyfill'ini kurun (React Native'in yerleşik URL uygulaması eksiktir ve MQTT.js broker URL'sini ayrıştırır):
npx create-expo-app@latest mqtt-demo
cd mqtt-demo
npx expo install react-native-url-polyfill
npm install mqtt
MQTT.js 5, paket export'larında kendi içinde bağımsız tarayıcı paketini işaret eden bir react-native girişi tanımlar; bu yüzden yeni Metro sürümleri import mqtt from "mqtt" ifadesini doğru çözümler. Paketleyiciniz bunun yerine Node derlemesini seçerse (net, tls veya fs ile ilgili hatalar görürsünüz), paketi açıkça import mqtt from "mqtt/dist/mqtt.esm" ile import edin.
Giriş dosyanızın en üstünde (Expo Router'da app/_layout.tsx, yalın bir uygulamada index.js) polyfill'i bir kez yükleyin:
import "react-native-url-polyfill/auto";
Bir React Native MQTT Hook'u Oluşturma
Özel bir hook, bağlantı mantığını bileşenlerin dışında tutar ve tek bir client'ı ekranlar arasında paylaşmayı kolaylaştırır. Bu sürüm mount anında bağlanır, subscribe olur, mesajları ve bağlantı durumunu sunar ve AppState değişikliklerine tepki verir.
hooks/useMqtt.js:
import { useEffect, useRef, useState, useCallback } from "react";
import { AppState } from "react-native";
import mqtt from "mqtt";
const BROKER_URL = "wss://broker.connectmq.com:8084/mqtt";
export function useMqtt({ topics, username, password, clientId }) {
const clientRef = useRef(null);
const [status, setStatus] = useState("idle");
const [messages, setMessages] = useState({});
const idRef = useRef(clientId || `rn-${Math.random().toString(16).slice(2, 10)}`);
const connect = useCallback(() => {
if (clientRef.current) return;
const client = mqtt.connect(BROKER_URL, {
clientId: idRef.current,
username,
password,
protocolVersion: 5,
clean: true,
keepalive: 30,
reconnectPeriod: 2000,
connectTimeout: 10000,
});
clientRef.current = client;
setStatus("connecting");
client.on("connect", () => {
setStatus("connected");
client.subscribe(topics, { qos: 1 }, (err) => {
if (err) console.warn("subscribe failed", err.message);
});
});
client.on("reconnect", () => setStatus("reconnecting"));
client.on("offline", () => setStatus("offline"));
client.on("error", (err) => console.warn("mqtt error", err.message));
client.on("message", (topic, payload) => {
setMessages((prev) => ({ ...prev, [topic]: payload.toString() }));
});
}, [topics, username, password]);
const disconnect = useCallback(() => {
const client = clientRef.current;
clientRef.current = null;
if (client) client.end(true);
setStatus("idle");
}, []);
const publish = useCallback((topic, message) => {
clientRef.current?.publish(topic, message, { qos: 1 });
}, []);
useEffect(() => {
connect();
const sub = AppState.addEventListener("change", (state) => {
if (state === "active") connect();
else if (state === "background") disconnect();
});
return () => {
sub.remove();
disconnect();
};
}, [connect, disconnect]);
return { status, messages, publish, clientId: idRef.current };
}
client.end(true) ifadesine dikkat edin: true, yoldaki mesajları beklemek yerine soketi hemen kapanmaya zorlar; bu önemlidir, çünkü işletim sistemi uygulamayı askıya almadan önce size yalnızca birkaç saniye tanır.
Arka Plan ve Ön Plan Yeniden Bağlanmalarını Yönetme
React Native MQTT uygulamalarının çoğu tam da burada hata yapar; bu yüzden bu konu kendi bölümünü hak ediyor. Bir telefonda gerçekte olan şudur:
- Kullanıcı uygulama değiştirir. iOS saniyeler içinde JavaScript thread'inizi askıya alır; Android onu kısa bir süre canlı tutabilir, ardından ağ erişimini kısıtlar.
- WebSocket uygulamanın bakış açısından "açık" kalır, ancak keep-alive ping'leri gönderilmez. Broker, oturumu yaklaşık 1,5 keep-alive aralığından sonra kapatır.
- Kullanıcı geri döner. MQTT.js ölü soketi sonunda fark eder, ama yalnızca kendi zaman aşımından sonra; bu yüzden arayüz birkaç saniye boyunca eski değerleri ve "connected" durumunu gösterir.
Hook bunu background durumunda bilinçli olarak bağlantıyı kapatıp active durumunda yeni bir bağlantı açarak önler. Birkaç kural daha yardımcı olur:
connectişleyicisinin içinde subscribe olun, client'ı oluşturduktan sonra bir kez değil. MQTT.js kendi yeniden bağlanmalarında varsayılan olarak yeniden subscribe olur, ancak ön plana dönüldükten sonra oluşturulan yepyeni bir client yeni bir subscribe gerektirir; bunu işleyicide yapmak iki durumu da kapsar.- Durum için retained mesajlar kullanın. Cihazlar en son durumlarını retain bayrağıyla publish ederse, yeni bağlanan bir uygulama bir sonraki güncellemeyi beklemeden güncel değeri hemen alır. Bkz. MQTT retained mesajları.
- Uyarılar için arka planda bağlantı tutmayın. Kapalı bir uygulamaya ulaşması gereken her şey için platform push bildirimlerini kullanın; MQTT, uygulama açıkken canlı veri içindir.
- iOS'ta
inactivedurumunu yok sayın. Bildirim merkezi veya gelen bir arama için tetiklenir; orada bağlantıyı kapatmak gereksiz yeniden bağlanma karmaşasına yol açar.
| AppState | Hook ne yapar | Neden |
|---|---|---|
active |
client yoksa connect() |
Taze soket ve subscription'lar |
inactive (yalnızca iOS) |
hiçbir şey | Geçici, ör. kontrol merkezi aşağı çekildi |
background |
disconnect() |
İşletim sistemi soketi zaten askıya alacak |
Hook'u Bir Ekranda Kullanma
import { View, Text, Switch, StyleSheet } from "react-native";
import { useMemo, useState } from "react";
import { useMqtt } from "../hooks/useMqtt";
const PREFIX = "connectmq/demo/rn-7f3a91"; // kendi rastgele önekinizi seçin
export default function DeviceScreen() {
const topics = useMemo(() => [`${PREFIX}/pump/status`, `${PREFIX}/tank/level`], []);
const { status, messages, publish } = useMqtt({ topics });
const [pumpOn, setPumpOn] = useState(false);
const level = messages[`${PREFIX}/tank/level`] ?? "--";
const pumpStatus = messages[`${PREFIX}/pump/status`] ?? "unknown";
return (
<View style={styles.container}>
<Text style={styles.status}>Broker: {status}</Text>
<Text style={styles.value}>{level} %</Text>
<Text>Tank level</Text>
<Text style={styles.row}>Pump reports: {pumpStatus}</Text>
<View style={styles.row}>
<Text>Pump command</Text>
<Switch
value={pumpOn}
disabled={status !== "connected"}
onValueChange={(v) => {
setPumpOn(v);
publish(`${PREFIX}/pump/set`, v ? "ON" : "OFF");
}}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 24, justifyContent: "center", gap: 8 },
status: { color: "#666" },
value: { fontSize: 56, fontWeight: "600" },
row: { marginTop: 16, flexDirection: "row", justifyContent: "space-between", alignItems: "center" },
});
Topic dizisindeki useMemo önemlidir: o olmadan her render'da oluşan yeni dizi connect callback'ini değiştirir ve yeniden bağlanma döngülerine neden olur.
Tek Bir Bağlantıyı Ekranlar Arasında Paylaşma
Hook, yazıldığı haliyle onu kullanan her bileşen için bir bağlantı açar. Birkaç ekranlı bir uygulamada client'ı kök seviyedeki bir React context provider'a taşıyın: provider, topic'lerin birleşimiyle useMqtt'yi bir kez çağırır ve messages ile publish'i context üzerinden sunar; her ekran yalnızca ihtiyaç duyduğu topic'leri okur. Uygulama başına tek bağlantı sizi bağlantı limitlerinin rahatça içinde tutar (ücretsiz Serverless planı toplamda 25 eşzamanlı bağlantıya izin verir) ve broker'ın tek bir telefondan birbiriyle yarışan birkaç oturum görmesini önler. Ekranların zamanla farklı subscription'lara ihtiyacı olursa, yeniden bağlanmak yerine context'e subscribe ve unsubscribe fonksiyonları ekleyin.
Public Broker'a Karşı Test
broker.connectmq.com adresindeki ücretsiz public MQTT broker anonim client'ları kabul eder; bu yüzden hesap olmadan test edebilirsiniz. Public alan herkes tarafından okunabildiği için rastgele öneki koruyun. Tankı ve pompayı bir terminalden simüle edin:
PREFIX="connectmq/demo/rn-7f3a91"
# Retained, böylece uygulama bağlandıktan hemen sonra görür
mosquitto_pub -h broker.connectmq.com -p 1883 -i rn-sim-tank -r \
-t "$PREFIX/tank/level" -m "64"
mosquitto_pub -h broker.connectmq.com -p 1883 -i rn-sim-pump -r \
-t "$PREFIX/pump/status" -m "OFF"
mosquitto_sub -h broker.connectmq.com -p 1883 -i rn-sim-cmd -t "$PREFIX/pump/set" -v
Uygulamayı bir dakikalığına arka plana alın, geri getirin ve durumun connecting'den connected'a geçtiğini ve retained değerlerin anında yeniden göründüğünü doğrulayın. Public broker client başına saniyede en fazla 10 mesaja izin verir; bu bir arayüz için fazlasıyla yeterlidir.
Üretim: Özel Broker ve Kimlik Bilgileri
Yayınlanan bir uygulama için yönetilen bir MQTT broker üzerinde özel bir broker'a geçin:
- ConnectMQ portalında bir broker oluşturun ve üretilen kullanıcı adını, parolayı ve client ID'yi kopyalayın.
- Bunları hook'a iletin:
useMqtt({ topics, username: "YOUR_USERNAME", password: "YOUR_PASSWORD", clientId: "YOUR_CLIENT_ID" }). - Gizli bilgileri
expo-secure-storeveya platform keychain'i ile saklayın; herkesin açabileceği JavaScript paketinde asla saklamayın.
ConnectMQ client ID'leri verdiği kimlik bilgilerine bağlar; bu yüzden iki telefon aynı anda tek bir kimliği paylaşamaz. Çok kullanıcılı bir uygulamada backend'inizin, oturum açtıktan sonra her kullanıcıya kendi kimlik bilgilerini vermesini sağlayın. Ücretsiz Serverless planı en fazla 25 eşzamanlı bağlantıya izin verir; bu bir ekip pilotu için yeterlidir; ücretli Serverless planları 1.000'e kadar çıkar.
Sorun Giderme
| Belirti | Neden | Çözüm |
|---|---|---|
Cannot read property 'protocol' of undefined veya URL hataları |
URL polyfill'i eksik | Önce react-native-url-polyfill/auto import edin |
net veya tls içeren hatalar |
Metro Node derlemesini çözümledi | mqtt/dist/mqtt.esm import edin |
| Simülatörde bağlanıyor, telefonda zaman aşımı | Yanlış port veya wss:// yerine ws:// |
wss://...:8084/mqtt kullanın |
| Her render'da yeniden bağlanma döngüsü | Kararsız topics dizisi |
useMemo içine sarın |
| Devam ettikten sonra eski veri | Soket arka planda öldü | background durumunda bağlantıyı kesin, active durumunda yeniden bağlanın |
ConnectMQ ile Başlayın
React Native uygulamanızı dakikalar içinde özel bir broker'a yönlendirin. app.connectmq.com adresinde kaydolun, kredi kartı gerekmeden ücretsiz bir Serverless broker oluşturun ve uygulamanızın komutlarının geldiğini tarayıcı içi client ve canlı topic explorer ile izleyin. ConnectMQ TLS'i, ölçeklendirmeyi ve broker operasyonlarını üstlenir; böylece sürdürdüğünüz tek MQTT kodu hook'unuz olur. Diğer yığınlar için MQTT client kütüphaneleri rehberi ve Flutter MQTT eğitimi aynı deseni gösterir.
Sık Sorulan Sorular
React Native, WebSocket yerine TCP üzerinden MQTT kullanabilir mi?
Yalnızca yerel bir TCP soket modülüyle; bu da yerel derleme karmaşıklığı ekler. 8084 portundaki güvenli WebSocket, MQTT.js ile kutudan çıktığı gibi çalışır ve daha basit, daha taşınabilir bir seçimdir.
Bu Expo Go ile çalışır mı?
Evet. WebSocket üzerinden MQTT.js saf JavaScript'tir; bu yüzden özel bir dev client olmadan Expo Go'da çalışır.
React Native MQTT bağlantım neden arka planda kopuyor?
Mobil işletim sistemleri arka plandaki uygulamaları ve ağ soketlerini askıya alır. background durumunda bağlantıyı kesin, active durumunda yeniden bağlanın ve kapalı bir uygulamaya ulaşması gereken uyarılar için push bildirimleri kullanın.
React Native'den MQTT 5 mi yoksa 3.1.1 mi kullanmalıyım?
Her ikisi de ConnectMQ ile çalışır. MQTT 5, bir şey başarısız olduğunda daha net reason code'lar verir; bu da mobilde bağlantı sorunlarını ayıklamayı kolaylaştırır.



