tutoriales.com

React Native y WebSockets: Comunicación en Tiempo Real con `ws` y `Socket.IO`

Este tutorial te guiará a través de la implementación de comunicación en tiempo real en tus aplicaciones React Native utilizando WebSockets. Exploraremos tanto la API nativa `ws` como la popular librería `Socket.IO` para conectar tu frontend móvil con un backend en tiempo real. Aprenderás a configurar el servidor y el cliente, enviar y recibir mensajes, y manejar eventos.

Intermedio20 min de lectura16 views
Reportar error

La comunicación en tiempo real es una característica fundamental para muchas aplicaciones modernas, desde chats hasta paneles de control interactivos y juegos. En el ecosistema de React Native, los WebSockets ofrecen una solución eficiente y bidireccional para lograr esta interactividad.

Este tutorial te sumergirá en el mundo de los WebSockets con React Native. Cubriremos dos enfoques principales: la API nativa de WebSockets (ws) y la librería Socket.IO, conocida por su robustez y características adicionales como la reconexión automática y el fallback a HTTP long-polling.

🚀 ¿Qué son los WebSockets?

Los WebSockets son un protocolo de comunicación que proporciona un canal de comunicación dúplex completo sobre una única conexión TCP. A diferencia de HTTP, que es unidireccional (cliente envía una petición, servidor responde), los WebSockets permiten que tanto el cliente como el servidor envíen datos de forma independiente en cualquier momento, una vez establecida la conexión. Esto los hace ideales para aplicaciones que requieren actualizaciones instantáneas.

💡 Consejo: Piensa en HTTP como una conversación por walkie-talkie (uno habla, el otro escucha), mientras que WebSockets es como una llamada telefónica bidireccional y continua.

Ventajas de los WebSockets:

  • Bidireccionalidad: Permiten el flujo de datos en ambas direcciones simultáneamente.
  • Baja latencia: Una vez establecida la conexión, los datos se envían sin la sobrecarga de los encabezados HTTP repetitivos.
  • Eficiencia: Menos consumo de recursos de red en comparación con el polling constante.
  • Persistencia: La conexión se mantiene abierta, eliminando la necesidad de reestablecerla para cada intercambio de datos.
HTTP WebSockets Cliente Servidor Request 1 Response 1 Request 2 Response 2 Cliente Servidor Handshake (HTTP Upgrade) CONEXIÓN ABIERTA Comunicación Bidireccional

🛠️ Configuración Inicial: Servidor y Cliente

Antes de sumergirnos en el código de React Native, necesitamos un servidor de WebSockets básico con el que nuestro cliente pueda interactuar. Usaremos Node.js para el servidor debido a su popularidad y facilidad de uso con librerías como ws y Socket.IO.

1. Preparando el Entorno de Desarrollo

Asegúrate de tener Node.js y npm (o yarn) instalados en tu sistema. Para el cliente React Native, necesitarás un entorno de desarrollo React Native funcional (Expo CLI o React Native CLI).

# Verifica Node.js y npm
node -v
npm -v

# Crea un nuevo proyecto React Native (si no tienes uno)
npx react-native init RealtimeApp
cd RealtimeApp

# O si prefieres Expo
npx create-expo-app RealtimeAppExpo
cd RealtimeAppExpo

🌐 Implementando WebSockets con la API Nativa (ws)

Empezaremos con la implementación más básica utilizando la API de WebSockets disponible tanto en navegadores como en React Native, y un servidor Node.js que usa la librería ws.

Servidor ws (Node.js)

Crea una nueva carpeta llamada server en la raíz de tu proyecto o en una ubicación separada. Dentro de server, inicializa un proyecto Node.js y instala la librería ws.

mkdir server
cd server
npm init -y
npm install ws

Crea un archivo index.js dentro de la carpeta server con el siguiente contenido:

// server/index.js
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', ws => {
  console.log('Cliente conectado');

  ws.on('message', message => {
    console.log(`Mensaje recibido: ${message}`);
    // Envía el mensaje de vuelta a todos los clientes conectados
    wss.clients.forEach(client => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(`Eco de ${message}`);
      }
    });
    // Envía un mensaje solo al cliente que lo envió
    ws.send(`Recibiste: ${message}`);
  });

  ws.on('close', () => {
    console.log('Cliente desconectado');
  });

  ws.on('error', error => {
    console.error('WebSocket error:', error);
  });

  ws.send('Bienvenido al servidor WebSocket nativo!');
});

console.log('Servidor WebSocket nativo iniciado en ws://localhost:8080');

Inicia el servidor:

node server/index.js

Cliente React Native (ws)

En tu proyecto React Native, el objeto global WebSocket ya está disponible. No necesitas instalar librerías adicionales para el cliente nativo.

Modifica tu archivo App.js (o App.tsx si usas TypeScript) para incluir el cliente WebSocket.

// App.js
import React, { useEffect, useState } from 'react';
import { SafeAreaView, Text, TextInput, Button, FlatList, View, StyleSheet } from 'react-native';

const WS_URL = 'ws://localhost:8080'; // Asegúrate de que esta URL sea accesible desde tu dispositivo/emulador

const App = () => {
  const [socket, setSocket] = useState(null);
  const [message, setMessage] = useState('');
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const ws = new WebSocket(WS_URL);

    ws.onopen = () => {
      console.log('Conectado al servidor WebSocket nativo');
      setMessages(prev => [...prev, { id: Date.now(), text: 'Conectado al servidor.', type: 'system' }]);
    };

    ws.onmessage = (event) => {
      console.log('Mensaje recibido del servidor:', event.data);
      setMessages(prev => [...prev, { id: Date.now(), text: event.data, type: 'server' }]);
    };

    ws.onerror = (error) => {
      console.error('Error del WebSocket:', error);
      setMessages(prev => [...prev, { id: Date.now(), text: `Error: ${error.message}`, type: 'error' }]);
    };

    ws.onclose = () => {
      console.log('Desconectado del servidor WebSocket nativo');
      setMessages(prev => [...prev, { id: Date.now(), text: 'Desconectado del servidor.', type: 'system' }]);
      // Opcional: intentar reconectar
      // setTimeout(() => setSocket(new WebSocket(WS_URL)), 3000);
    };

    setSocket(ws);

    return () => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.close();
      }
    };
  }, []);

  const sendMessage = () => {
    if (socket && socket.readyState === WebSocket.OPEN && message.trim()) {
      socket.send(message.trim());
      setMessages(prev => [...prev, { id: Date.now(), text: `Yo: ${message.trim()}`, type: 'client' }]);
      setMessage('');
    }
  };

  const renderMessage = ({ item }) => (
    <View style={styles.messageContainer}>
      <Text style={[styles.messageText, styles[item.type]]}>{item.text}</Text>
    </View>
  );

  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.header}>React Native WebSocket Nativo</Text>
      <FlatList
        data={messages}
        renderItem={renderMessage}
        keyExtractor={(item) => item.id.toString()}
        style={styles.messageList}
        inverted // Para mostrar los mensajes más recientes abajo
      />
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.input}
          value={message}
          onChangeText={setMessage}
          placeholder="Escribe un mensaje..."
          placeholderTextColor="#999"
        />
        <Button title="Enviar" onPress={sendMessage} disabled={!socket || socket.readyState !== WebSocket.OPEN} />
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f0f2f5',
    paddingTop: 20,
  },
  header: {
    fontSize: 22,
    fontWeight: 'bold',
    textAlign: 'center',
    marginVertical: 15,
    color: '#333',
  },
  messageList: {
    flex: 1,
    paddingHorizontal: 10,
  },
  messageContainer: {
    paddingVertical: 5,
  },
  messageText: {
    fontSize: 16,
    padding: 8,
    borderRadius: 10,
    marginBottom: 5,
    maxWidth: '80%',
  },
  system: {
    alignSelf: 'center',
    backgroundColor: '#e0e0e0',
    color: '#555',
    textAlign: 'center',
  },
  server: {
    alignSelf: 'flex-start',
    backgroundColor: '#dcf8c6',
    color: '#333',
  },
  client: {
    alignSelf: 'flex-end',
    backgroundColor: '#add8e6',
    color: '#333',
  },
  error: {
    alignSelf: 'center',
    backgroundColor: '#ffdddd',
    color: '#cc0000',
  },
  inputContainer: {
    flexDirection: 'row',
    padding: 10,
    borderTopWidth: 1,
    borderTopColor: '#ccc',
    backgroundColor: '#fff',
  },
  input: {
    flex: 1,
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 20,
    paddingHorizontal: 15,
    marginRight: 10,
    fontSize: 16,
    color: '#333',
  },
});

export default App;
⚠️ Advertencia: Para que `ws://localhost:8080` funcione en tu emulador o dispositivo, `localhost` debe ser reemplazado por la dirección IP de tu máquina en la red local. Si usas un emulador Android, `10.0.2.2` suele apuntar a `localhost`. Para iOS o dispositivos reales, necesitarás la IP real de tu máquina (por ejemplo, `ws://192.168.1.5:8080`).

✨ Mejorando con Socket.IO

Aunque la API nativa ws es funcional, Socket.IO es una librería popular que ofrece una capa de abstracción sobre WebSockets, añadiendo características como:

  • Reconexión automática.
  • Detección de desconexiones.
  • Fallback a HTTP long-polling si WebSockets no están disponibles.
  • Envío de eventos personalizados.
  • Manejo de namespaces y rooms para una mejor organización.

Servidor Socket.IO (Node.js)

En tu carpeta server, instala socket.io y cors (para evitar problemas de CORS).

npm install socket.io cors

Modifica server/index.js o crea un nuevo archivo server/socketio-server.js:

// server/socketio-server.js
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const cors = require('cors');

const app = express();
app.use(cors()); // Habilita CORS para todas las rutas

const server = http.createServer(app);

// Configura Socket.IO
const io = new Server(server, {
  cors: {
    origin: "*", // Permite conexiones desde cualquier origen
    methods: ["GET", "POST"]
  }
});

io.on('connection', (socket) => {
  console.log(`Usuario conectado: ${socket.id}`);

  // Emitir un mensaje de bienvenida solo al cliente que se conectó
  socket.emit('welcome', `Bienvenido al servidor Socket.IO, ${socket.id}!`);

  // Manejar el evento 'chat message' del cliente
  socket.on('chat message', (msg) => {
    console.log(`Mensaje de ${socket.id}: ${msg}`);
    // Emitir el mensaje a todos los clientes conectados, excepto al que lo envió
    socket.broadcast.emit('chat message', { id: socket.id, message: msg });
    // Emitir el mensaje de vuelta al cliente que lo envió (para confirmación local)
    socket.emit('my message', { id: socket.id, message: msg });
  });

  // Manejar el evento 'disconnect'
  socket.on('disconnect', () => {
    console.log(`Usuario desconectado: ${socket.id}`);
    // Notificar a todos los demás clientes que un usuario se ha desconectado
    io.emit('system message', `Usuario ${socket.id} se ha desconectado.`);
  });

  // Enviar un mensaje del sistema a todos los usuarios cuando alguien se conecta
  socket.broadcast.emit('system message', `Usuario ${socket.id} se ha conectado.`);
});

const PORT = 3000;
server.listen(PORT, () => {
  console.log(`Servidor Socket.IO escuchando en http://localhost:${PORT}`);
});

Inicia este servidor:

node server/socketio-server.js

Cliente React Native (Socket.IO)

Para el cliente React Native, usaremos la librería socket.io-client.

cd RealtimeApp # o RealtimeAppExpo
npm install socket.io-client
# Para Expo, es posible que necesites instalar algunas dependencias para el cliente HTTP subyacente
# npx expo install @react-native-async-storage/async-storage # ejemplo si encuentras errores

Ahora, actualiza App.js para usar socket.io-client.

// App.js (versión Socket.IO)
import React, { useEffect, useState, useRef } from 'react';
import { SafeAreaView, Text, TextInput, Button, FlatList, View, StyleSheet, KeyboardAvoidingView, Platform } from 'react-native';
import io from 'socket.io-client';

const SOCKET_URL = 'http://localhost:3000'; // Asegúrate de que esta URL sea accesible

const App = () => {
  const [socket, setSocket] = useState(null);
  const [message, setMessage] = useState('');
  const [messages, setMessages] = useState([]);
  const flatListRef = useRef(null);

  useEffect(() => {
    const newSocket = io(SOCKET_URL, {
      transports: ['websocket'], // Forzar el uso de WebSockets en RN
      forceNew: true, // Para asegurar una nueva conexión cada vez que se monta
    });

    newSocket.on('connect', () => {
      console.log('Conectado a Socket.IO. ID:', newSocket.id);
      setMessages(prev => [...prev, { id: Date.now(), text: `Conectado al servidor. ID: ${newSocket.id}`, type: 'system' }]);
    });

    newSocket.on('welcome', (msg) => {
      setMessages(prev => [...prev, { id: Date.now(), text: msg, type: 'server' }]);
    });

    newSocket.on('chat message', (data) => {
      // Recibido de otros usuarios
      setMessages(prev => [...prev, { id: Date.now(), text: `Usuario ${data.id.substring(0, 4)}: ${data.message}`, type: 'other' }]);
    });

    newSocket.on('my message', (data) => {
      // Confirmación de mi propio mensaje enviado
      setMessages(prev => [...prev, { id: Date.now(), text: `Yo: ${data.message}`, type: 'client' }]);
    });

    newSocket.on('system message', (msg) => {
      setMessages(prev => [...prev, { id: Date.now(), text: msg, type: 'system' }]);
    });

    newSocket.on('disconnect', () => {
      console.log('Desconectado de Socket.IO');
      setMessages(prev => [...prev, { id: Date.now(), text: 'Desconectado del servidor.', type: 'system' }]);
    });

    newSocket.on('connect_error', (err) => {
      console.error('Error de conexión Socket.IO:', err.message);
      setMessages(prev => [...prev, { id: Date.now(), text: `Error de conexión: ${err.message}`, type: 'error' }]);
    });

    setSocket(newSocket);

    return () => {
      newSocket.disconnect();
    };
  }, []);

  useEffect(() => {
    if (flatListRef.current) {
      flatListRef.current.scrollToEnd({ animated: true });
    }
  }, [messages]);

  const sendMessage = () => {
    if (socket && message.trim()) {
      socket.emit('chat message', message.trim());
      setMessage('');
    }
  };

  const renderMessage = ({ item }) => {
    let messageStyle;
    switch (item.type) {
      case 'system':
        messageStyle = styles.system;
        break;
      case 'server':
        messageStyle = styles.server;
        break;
      case 'client':
        messageStyle = styles.client;
        break;
      case 'other':
        messageStyle = styles.other;
        break;
      case 'error':
        messageStyle = styles.error;
        break;
      default:
        messageStyle = styles.system;
    }
    return (
      <View style={styles.messageContainer}>
        <Text style={[styles.messageText, messageStyle]}>{item.text}</Text>
      </View>
    );
  };

  return (
    <SafeAreaView style={styles.container}>
      <KeyboardAvoidingView
        behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
        style={{ flex: 1 }}
        keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 20}
      >
        <Text style={styles.header}>React Native Socket.IO Chat</Text>
        <FlatList
          ref={flatListRef}
          data={messages}
          renderItem={renderMessage}
          keyExtractor={(item) => item.id.toString()}
          style={styles.messageList}
          contentContainerStyle={{ paddingBottom: 10 }}
        />
        <View style={styles.inputContainer}>
          <TextInput
            style={styles.input}
            value={message}
            onChangeText={setMessage}
            placeholder="Escribe un mensaje..."
            placeholderTextColor="#999"
            editable={socket && socket.connected}
          />
          <Button title="Enviar" onPress={sendMessage} disabled={!socket || !socket.connected || !message.trim()} />
        </View>
      </KeyboardAvoidingView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f0f2f5',
    paddingTop: 20,
  },
  header: {
    fontSize: 22,
    fontWeight: 'bold',
    textAlign: 'center',
    marginVertical: 15,
    color: '#333',
  },
  messageList: {
    flex: 1,
    paddingHorizontal: 10,
  },
  messageContainer: {
    paddingVertical: 5,
  },
  messageText: {
    fontSize: 16,
    padding: 8,
    borderRadius: 10,
    marginBottom: 5,
    maxWidth: '80%',
  },
  system: {
    alignSelf: 'center',
    backgroundColor: '#e0e0e0',
    color: '#555',
    textAlign: 'center',
  },
  server: {
    alignSelf: 'flex-start',
    backgroundColor: '#dcf8c6',
    color: '#333',
  },
  client: {
    alignSelf: 'flex-end',
    backgroundColor: '#add8e6',
    color: '#333',
  },
  other: {
    alignSelf: 'flex-start',
    backgroundColor: '#fff',
    color: '#333',
    borderColor: '#ddd',
    borderWidth: 1,
  },
  error: {
    alignSelf: 'center',
    backgroundColor: '#ffdddd',
    color: '#cc0000',
  },
  inputContainer: {
    flexDirection: 'row',
    padding: 10,
    borderTopWidth: 1,
    borderTopColor: '#ccc',
    backgroundColor: '#fff',
  },
  input: {
    flex: 1,
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 20,
    paddingHorizontal: 15,
    marginRight: 10,
    fontSize: 16,
    color: '#333',
  },
});

export default App;
🔥 Importante: Al igual que con `ws`, si tu servidor Node.js no se ejecuta en `localhost` y estás utilizando un emulador o dispositivo real, deberás reemplazar `http://localhost:3000` con la dirección IP de tu máquina host (ej. `http://192.168.1.5:3000`). Para emuladores Android, `http://10.0.2.2:3000` suele funcionar.

🔍 Comparativa entre ws y Socket.IO

Ambas opciones son válidas, pero tienen sus casos de uso. Aquí una tabla comparativa:

CaracterísticaWebSocket (API nativa/ws)Socket.IO
---------
ComplejidadMás bajo nivel, requiere más manejo manualMás alto nivel, abstrae detalles de la conexión
FiabilidadRequiere manejo manual de reconexión, fallosReconexión automática, detección de desconexión
---------
FallbackSolo WebSocketsWebSockets, HTTP Long Polling, etc.
APIonopen, onmessage, onerror, onclose, sendon, emit, namespaces, rooms
---------
Eventos PersonalizadosNo directamente, requiere serialización manualSí, con emit/on
SobrecargaMás ligeroLigeramente más sobrecarga por las características adicionales
---------
UsoAplicaciones sencillas, control granularAplicaciones complejas, chats, juegos, colaboración en tiempo real

📈 Casos de Uso Comunes

Los WebSockets en React Native abren un mundo de posibilidades para la interactividad en tus aplicaciones:

  • Aplicaciones de Chat: La transmisión instantánea de mensajes es el caso de uso más obvio.
  • Notificaciones en Tiempo Real: Envía notificaciones a los usuarios sin necesidad de polling.
  • Juegos Multijugador: Sincroniza estados de juego entre jugadores.
  • Paneles de Control: Actualiza gráficos y datos en tiempo real para monitorización.
  • Colaboración: Edición conjunta de documentos, pizarras interactivas.
  • Streaming de Datos: Acciones de bolsa, resultados deportivos en vivo.
90% - Cobertura de Casos de Uso

🔒 Consideraciones de Seguridad

Al trabajar con WebSockets, la seguridad es clave:

  • WSS (WebSocket Secure): Siempre usa wss:// en producción. Esto cifra la comunicación a través de TLS/SSL, al igual que HTTPS.
  • Autenticación y Autorización: Implementa mecanismos para asegurar que solo los usuarios autorizados puedan conectarse y enviar/recibir mensajes. Esto puede hacerse con tokens JWT enviados durante el handshake inicial o a través de los mensajes.
  • Validación de Entrada: Valida todos los mensajes recibidos del cliente en el servidor para prevenir ataques como inyección de código o desbordamiento de búfer.
  • Rate Limiting: Limita la cantidad de mensajes que un cliente puede enviar en un período de tiempo para prevenir ataques DoS.
⚠️ Advertencia: Nunca expongas tu servidor WebSocket sin las medidas de seguridad adecuadas en un entorno de producción.

💡 Ejemplos Avanzados y Patrones

Manejo de Estados con Context API o Redux

En aplicaciones más complejas, es útil integrar los mensajes de WebSocket con tu sistema de gestión de estado global (Context API, Redux, Zustand, etc.). Puedes despachar acciones o actualizar el estado directamente desde tus listeners de onmessage o socket.on.

// Ejemplo simplificado con Context API
// Context/SocketContext.js
import React, { createContext, useContext, useEffect, useState } from 'react';
import io from 'socket.io-client';

const SocketContext = createContext();

export const useSocket = () => useContext(SocketContext);

export const SocketProvider = ({ children }) => {
  const [socket, setSocket] = useState(null);
  const [isSocketConnected, setIsSocketConnected] = useState(false);
  const [receivedMessages, setReceivedMessages] = useState([]);

  useEffect(() => {
    const newSocket = io('http://localhost:3000', { transports: ['websocket'] });

    newSocket.on('connect', () => {
      setIsSocketConnected(true);
      console.log('Socket Context: Conectado');
    });

    newSocket.on('disconnect', () => {
      setIsSocketConnected(false);
      console.log('Socket Context: Desconectado');
    });

    newSocket.on('chat message', (data) => {
      setReceivedMessages(prev => [...prev, { id: Date.now(), text: `Otro: ${data.message}`, type: 'other' }]);
    });

    setSocket(newSocket);

    return () => {
      newSocket.disconnect();
    };
  }, []);

  const sendMessage = (msg) => {
    if (socket && isSocketConnected) {
      socket.emit('chat message', msg);
      setReceivedMessages(prev => [...prev, { id: Date.now(), text: `Yo: ${msg}`, type: 'client' }]);
    }
  };

  return (
    <SocketContext.Provider value={{ socket, isSocketConnected, receivedMessages, sendMessage }}>
      {children}
    </SocketContext.Provider>
  );
};

// En tu App.js (o un componente hijo)
// <SocketProvider>
//   <YourChatComponent />
// </SocketProvider>

// Dentro de YourChatComponent
// const { isSocketConnected, receivedMessages, sendMessage } = useSocket();
// ... UI con mensajes y función sendMessage

Usando Rooms y Namespaces (Socket.IO)

¿Qué son las *Rooms* y *Namespaces*? Socket.IO permite organizar tus conexiones con *namespaces* (rutas lógicas para dividir la aplicación, ej. `/admin`, `/chat`) y *rooms* (grupos de sockets dentro de un *namespace*, ej. `room1`, `general`). Esto es útil para escalar y gestionar eventos específicos para subconjuntos de usuarios. Por ejemplo, en un chat, cada conversación podría ser una *room* diferente.

Ejemplo de Servidor con Rooms (Socket.IO):

// server/socketio-rooms-server.js
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const cors = require('cors');

const app = express();
app.use(cors());
const server = http.createServer(app);

const io = new Server(server, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"]
  }
});

io.on('connection', (socket) => {
  console.log(`Usuario conectado: ${socket.id}`);

  socket.on('joinRoom', (roomName) => {
    // Dejar cualquier sala anterior y unirse a la nueva
    socket.rooms.forEach(room => {
      if (room !== socket.id) { // socket.id es la sala predeterminada de cada socket
        socket.leave(room);
        console.log(`Usuario ${socket.id} ha dejado la sala ${room}`);
      }
    });

    socket.join(roomName);
    console.log(`Usuario ${socket.id} se ha unido a la sala ${roomName}`);
    io.to(roomName).emit('system message', `Usuario ${socket.id} se ha unido a ${roomName}.`);
    socket.emit('joinedRoom', roomName);
  });

  socket.on('roomMessage', ({ room, message }) => {
    console.log(`Mensaje en sala ${room} de ${socket.id}: ${message}`);
    io.to(room).emit('roomMessage', { id: socket.id, room: room, message: message });
  });

  socket.on('disconnect', () => {
    console.log(`Usuario desconectado: ${socket.id}`);
    // Al desconectarse, emitir a todas las salas en las que estaba
    socket.rooms.forEach(room => {
      if (room !== socket.id) {
        io.to(room).emit('system message', `Usuario ${socket.id} se ha desconectado de ${room}.`);
      }
    });
  });

  socket.emit('welcome', `Bienvenido al servidor Socket.IO con salas!`);
});

const PORT = 3001; // Puerto diferente para no colisionar
server.listen(PORT, () => {
  console.log(`Servidor Socket.IO con salas escuchando en http://localhost:${PORT}`);
});

Ejemplo de Cliente con Rooms (React Native Socket.IO):

Podrías añadir un TextInput para ingresar el nombre de la sala y un botón para unirse a ella, y luego enviar mensajes específicos a esa sala.

// En App.js (parte relevante para rooms)
// ... imports y useState ...
const [currentRoom, setCurrentRoom] = useState('general');
const [roomInput, setRoomInput] = useState('');

useEffect(() => {
  // ... configuración de socket ...
  if (newSocket) {
    newSocket.on('joinedRoom', (roomName) => {
      setCurrentRoom(roomName);
      setMessages(prev => [...prev, { id: Date.now(), text: `Has entrado en la sala: ${roomName}`, type: 'system' }]);
    });
    newSocket.on('roomMessage', (data) => {
      if (data.room === currentRoom) {
        const sender = data.id === socket.id ? 'Yo' : `Usuario ${data.id.substring(0, 4)}`;
        setMessages(prev => [...prev, { id: Date.now(), text: `${sender} (${data.room}): ${data.message}`, type: data.id === socket.id ? 'client' : 'other' }]);
      }
    });
  }
  // ... resto del useEffect ...
}, [socket]); // Asegúrate de que este useEffect depende de `socket` para re-ejecutarse si el socket cambia

const joinRoom = () => {
  if (socket && roomInput.trim()) {
    socket.emit('joinRoom', roomInput.trim());
    setRoomInput('');
  }
};

const sendRoomMessage = () => {
  if (socket && message.trim() && currentRoom) {
    socket.emit('roomMessage', { room: currentRoom, message: message.trim() });
    setMessage('');
  }
};

// ... en el return de tu componente ...
<View style={styles.roomInputContainer}>
  <TextInput
    style={styles.input}
    value={roomInput}
    onChangeText={setRoomInput}
    placeholder="Nombre de la sala..."
    placeholderTextColor="#999"
  />
  <Button title="Unirse a Sala" onPress={joinRoom} disabled={!socket || !socket.connected || !roomInput.trim()} />
</View>
<Text style={styles.currentRoomText}>Sala actual: <mark>{currentRoom}</mark></Text>
// ... cambiar el botón de enviar mensaje a sendRoomMessage ...
<Button title="Enviar" onPress={sendRoomMessage} disabled={!socket || !socket.connected || !message.trim()} />

// Añadir estilos para roomInputContainer y currentRoomText
// ...
Servidor Socket.IO Cliente 1 (React Native) Cliente 2 (React Native) Room A Room B Socket.emit Broadcast Events

🏁 Conclusión

Hemos explorado la implementación de comunicación en tiempo real en React Native utilizando tanto la API nativa de WebSockets como la potente librería Socket.IO. Mientras que ws te da un control más granular y es más ligero, Socket.IO simplifica gran parte de la complejidad, ofreciendo características esenciales para aplicaciones robustas como la reconexión automática, namespaces y rooms.

La elección entre uno y otro dependerá de los requisitos específicos de tu proyecto, pero en la mayoría de los casos de aplicaciones en tiempo real, Socket.IO será la opción más conveniente por su estabilidad y funcionalidades añadidas. ¡Ahora estás listo para construir aplicaciones React Native interactivas y dinámicas!

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!