IoT-Tutorials

React Native MQTT: Mobile IoT-Apps in Echtzeit über WebSocket

Bauen Sie eine React Native MQTT App mit MQTT.js über sicheren WebSocket: ein wiederverwendbarer Hook, Live-Gerätedaten, Befehle und korrektes Reconnect-Handling beim Wechsel zwischen Hintergrund und Vordergrund.

React Native MQTT App auf Android und iPhone, die Live-IoT-Daten von einem Cloud-MQTT-Broker über sicheren WebSocket empfängt
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

Mit React Native MQTT erhält Ihre mobile App Live-Gerätedaten, ohne eine API pollen zu müssen. In diesem Tutorial verbinden Sie eine React-Native- (oder Expo-)App mit MQTT.js über sicheren WebSocket mit einem Cloud-MQTT-Broker, kapseln die Logik in einen wiederverwendbaren Hook, senden Befehle an ein Gerät und behandeln die Übergänge zwischen Hintergrund und Vordergrund, an denen die meisten ersten Versuche scheitern. Das Muster eignet sich für Smart-Home-Apps ebenso wie für eine Instandhaltungs-App, mit der Techniker in der Halle Maschinenzustände und Füllstände live auf dem Smartphone verfolgen.

Warum WebSocket für React Native MQTT

Die JavaScript-Laufzeit von React Native bietet keine rohen TCP-Sockets. Sie können sie über ein natives Modul nachrüsten, doch das bedeutet native Build-Schritte, Expo Dev Clients und plattformspezifische Bugs. React Native liefert jedoch einen standardkonformen WebSocket mit, und MQTT.js kann MQTT bereits darüber betreiben. Das Ergebnis ist ein reiner JavaScript-Client, der ohne nativen Code in Expo Go, unter Android und unter iOS funktioniert.

ConnectMQ stellt sicheren WebSocket auf Port 8084 mit dem Pfad /mqtt und einem öffentlich vertrauenswürdigen Zertifikat bereit. Die benötigte URL lautet also:

wss://broker.connectmq.com:8084/mqtt

Port 8084 passiert außerdem die meisten Firmennetzwerke und Mobilfunknetze, weil er wie HTTPS aussieht. Den vollständigen Hintergrund finden Sie unter MQTT über WebSocket.

Projekt einrichten

Erstellen Sie ein Projekt und installieren Sie MQTT.js sowie ein URL-Polyfill (die eingebaute URL-Implementierung von React Native ist unvollständig, und MQTT.js parst die Broker-URL):

npx create-expo-app@latest mqtt-demo
cd mqtt-demo
npx expo install react-native-url-polyfill
npm install mqtt

MQTT.js 5 deklariert in seinen Package-Exports einen react-native-Eintrag, der auf sein eigenständiges Browser-Bundle zeigt, sodass aktuelle Metro-Versionen import mqtt from "mqtt" korrekt auflösen. Greift Ihr Bundler stattdessen auf den Node-Build zurück (erkennbar an Fehlern zu net, tls oder fs), importieren Sie das Bundle explizit mit import mqtt from "mqtt/dist/mqtt.esm".

Laden Sie das Polyfill einmal ganz oben in Ihrer Einstiegsdatei (app/_layout.tsx bei Expo Router oder index.js in einer Bare-App):

import "react-native-url-polyfill/auto";

Einen React Native MQTT Hook bauen

Ein eigener Hook hält die Verbindungslogik aus den Komponenten heraus und erleichtert es, einen Client über mehrere Screens hinweg zu teilen. Diese Version verbindet sich beim Mounten, abonniert Topics, stellt Nachrichten und Verbindungsstatus bereit und reagiert auf Änderungen von AppState.

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 };
}

Beachten Sie client.end(true): Das true schließt den Socket sofort, statt auf noch laufende Nachrichten zu warten. Das ist wichtig, weil das Betriebssystem Ihnen nur wenige Sekunden lässt, bevor es die App suspendiert.

Reconnects zwischen Hintergrund und Vordergrund

Hier machen die meisten React Native MQTT Apps Fehler, deshalb bekommt das Thema einen eigenen Abschnitt. Das passiert tatsächlich auf einem Telefon:

  1. Der Nutzer wechselt die App. Innerhalb von Sekunden suspendiert iOS Ihren JavaScript-Thread; Android hält ihn eventuell kurz am Leben und schränkt dann den Netzwerkzugriff ein.
  2. Aus Sicht der App bleibt der WebSocket "offen", aber es werden keine Keep-Alive-Pings mehr gesendet. Der Broker schließt die Session nach etwa dem 1,5-Fachen des Keep-Alive-Intervalls.
  3. Der Nutzer kehrt zurück. MQTT.js bemerkt den toten Socket irgendwann, aber erst nach seinem eigenen Timeout, sodass die UI mehrere Sekunden lang veraltete Werte und "connected" anzeigt.

Der Hook vermeidet das, indem er bei background bewusst trennt und bei active eine frische Verbindung öffnet. Einige weitere Regeln helfen:

  • Abonnieren Sie im connect-Handler, nicht einmalig nach dem Erstellen des Clients. MQTT.js abonniert bei eigenen Reconnects standardmäßig neu, ein brandneuer Client nach der Rückkehr in den Vordergrund braucht aber ein frisches Subscribe, und der Handler deckt beide Fälle ab.
  • Nutzen Sie Retained Messages für Zustände. Publizieren Geräte ihren letzten Status mit Retain-Flag, erhält eine frisch verbundene App den aktuellen Wert sofort, statt auf das nächste Update zu warten. Siehe MQTT Retained Messages.
  • Halten Sie für Alarme keine Verbindungen im Hintergrund offen. Nutzen Sie Push-Benachrichtigungen der Plattform für alles, was eine geschlossene App erreichen muss; MQTT ist für Live-Daten bei geöffneter App gedacht.
  • Ignorieren Sie inactive unter iOS. Dieser Zustand tritt beim Öffnen der Mitteilungszentrale oder bei einem eingehenden Anruf auf; wer dort die Verbindung abbaut, verursacht unnötige Reconnects.
AppState Was der Hook tut Warum
active connect(), falls kein Client existiert Frischer Socket und frische Abonnements
inactive (nur iOS) nichts Vorübergehend, z. B. heruntergezogenes Kontrollzentrum
background disconnect() Das Betriebssystem suspendiert den Socket ohnehin

Den Hook in einem Screen verwenden

import { View, Text, Switch, StyleSheet } from "react-native";
import { useMemo, useState } from "react";
import { useMqtt } from "../hooks/useMqtt";

const PREFIX = "connectmq/demo/rn-7f3a91";   // eigenes zufälliges Präfix wählen

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" },
});

useMemo für das Topic-Array ist wichtig: Ohne es verändert ein neues Array bei jedem Render den connect-Callback und verursacht Reconnect-Schleifen.

Eine Verbindung über mehrere Screens teilen

Der Hook in dieser Form öffnet eine Verbindung pro Komponente, die ihn nutzt. Bei einer App mit mehreren Screens heben Sie den Client in einen React-Context-Provider an der Wurzel: Der Provider ruft useMqtt einmal mit der Vereinigungsmenge aller Topics auf und stellt messages und publish über den Context bereit, und jeder Screen liest nur die Topics, die er braucht. Eine Verbindung pro App hält Sie deutlich unter den Verbindungslimits, denn der kostenlose Serverless-Plan erlaubt insgesamt 25 gleichzeitige Verbindungen, und sie verhindert, dass der Broker mehrere konkurrierende Sessions von einem Telefon sieht. Brauchen Screens im Laufe der Zeit unterschiedliche Abonnements, ergänzen Sie den Context um Funktionen für subscribe und unsubscribe, statt neu zu verbinden.

Gegen den öffentlichen MQTT-Broker testen

Der kostenlose öffentliche MQTT-Broker unter broker.connectmq.com akzeptiert anonyme Clients, sodass Sie ohne Konto testen können. Er ist für Tests und Prototypen gedacht, nicht für Produktionsdaten. Behalten Sie das zufällige Präfix bei, denn der öffentliche MQTT-Broker ist für alle lesbar. Simulieren Sie Tank und Pumpe aus einem Terminal:

PREFIX="connectmq/demo/rn-7f3a91"

# Retained, damit die App den Wert direkt nach dem Verbinden sieht
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

Schicken Sie die App für eine Minute in den Hintergrund, holen Sie sie zurück und prüfen Sie, dass der Status von connecting zu connected wechselt und die retained Werte sofort wieder erscheinen. Der öffentliche MQTT-Broker erlaubt bis zu 10 Nachrichten pro Sekunde und Client, was für eine UI mehr als genug ist.

Produktion: privater Broker und Zugangsdaten

Für eine ausgelieferte App wechseln Sie zu einem privaten Broker auf einem verwalteten MQTT-Broker:

  1. Erstellen Sie im ConnectMQ-Portal einen Broker und kopieren Sie den generierten Benutzernamen, das Passwort und die Client ID.
  2. Übergeben Sie sie an den Hook: useMqtt({ topics, username: "YOUR_USERNAME", password: "YOUR_PASSWORD", clientId: "YOUR_CLIENT_ID" }).
  3. Speichern Sie Secrets mit expo-secure-store oder in der Keychain der Plattform, niemals im JavaScript-Bundle, das jeder entpacken kann.

ConnectMQ bindet Client IDs an die ausgegebenen Zugangsdaten, sodass zwei Telefone nicht gleichzeitig dieselbe Identität nutzen können. Eindeutige Identitäten pro Gerät erleichtern außerdem die Nachvollziehbarkeit, die Sicherheitskonzepte nach IEC 62443 in Werksumgebungen verlangen. Bei einer Mehrbenutzer-App sollte Ihr Backend jedem Nutzer nach dem Login eigene Zugangsdaten übergeben. Der kostenlose Serverless-Plan erlaubt bis zu 25 gleichzeitige Verbindungen, was für einen Team-Pilot reicht; kostenpflichtige Serverless-Pläne gehen bis 1.000.

Fehlerbehebung

Symptom Ursache Lösung
Cannot read property 'protocol' of undefined oder URL-Fehler URL-Polyfill fehlt react-native-url-polyfill/auto zuerst importieren
Fehler mit Bezug auf net oder tls Metro hat den Node-Build aufgelöst mqtt/dist/mqtt.esm importieren
Verbindet im Simulator, Timeout auf dem Telefon Falscher Port oder ws:// statt wss:// wss://...:8084/mqtt verwenden
Reconnect-Schleife bei jedem Render Instabiles topics-Array In useMemo kapseln
Veraltete Daten nach dem Fortsetzen Socket ist im Hintergrund gestorben Bei background trennen, bei active neu verbinden

Starten Sie mit ConnectMQ

Verbinden Sie Ihre React-Native-App in wenigen Minuten mit einem privaten Broker. Registrieren Sie sich unter app.connectmq.com, erstellen Sie ohne Kreditkarte einen kostenlosen Serverless Broker und beobachten Sie mit dem Browser-Client und dem Live-Topic-Explorer, wie die Befehle Ihrer App ankommen. ConnectMQ kümmert sich um TLS, Skalierung und Broker-Betrieb, sodass Ihr Hook der einzige MQTT-Code bleibt, den Sie pflegen. Für andere Stacks zeigen der Leitfaden zu MQTT-Client-Bibliotheken und das Flutter MQTT Tutorial dasselbe Muster.

Häufig gestellte Fragen

Kann React Native MQTT über TCP statt WebSocket nutzen?

Nur mit einem nativen TCP-Socket-Modul, das zusätzliche native Build-Komplexität mit sich bringt. Sicherer WebSocket auf Port 8084 funktioniert mit MQTT.js sofort und ist die einfachere, portablere Wahl.

Funktioniert das mit Expo Go?

Ja. MQTT.js über WebSocket ist reines JavaScript und läuft daher ohne eigenen Dev Client in Expo Go.

Warum bricht meine React Native MQTT Verbindung im Hintergrund ab?

Mobile Betriebssysteme suspendieren Apps im Hintergrund samt ihren Netzwerk-Sockets. Trennen Sie bei background, verbinden Sie bei active neu und nutzen Sie Push-Benachrichtigungen für Alarme, die eine geschlossene App erreichen müssen.

Sollte ich in React Native MQTT 5 oder 3.1.1 verwenden?

Beides funktioniert mit ConnectMQ. MQTT 5 liefert bei Fehlern klarere Reason Codes, was die Fehlersuche bei Verbindungsproblemen auf Mobilgeräten erleichtert.

Verwandte Artikel

Die besten MQTT-CLI-Tools zum Testen, Skripten und Debuggen

Ein praxisnaher Vergleich von mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, der MQTT.js-CLI, curl und mqttui – mit demselben Test gegen broker.connectmq.com, damit Sie das richtige Tool zum Skripten und Debuggen wählen.

8 Min. Lesezeit

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.