node.js

ios

sockets

react-native

socket.io

I was working on an app with Phonegap + React.js and Socket.io. However, then React-Native got released and the native feel is amazing.

I tried getting socket.io-client working with React Native, but unfortunately without much success. I did some research and I'm getting the exact same errors as described in this issue: https://github.com/facebook/react-native/issues/375

The comments on the issue said to try and use the fetch API to fetch JS modules, but I think I'm doing this wrong:

var socketScript;    
fetch('https://cdn.socket.io/socket.io-1.2.0.js')
    .then(function(response) {
        socketScript = response._bodyText;
    }).done(function() {
        var socket = socketScript.io();
    });

This returns an undefined is not a function.

Is there any way to make socket.io-client work with React Native? Or am I looking at this the wrong way? Perhaps there are other, better suited solutions?

Solution 1

For those like me stumbling across this question looking how to integrate socket.io with react native.

Since React Native has supported websockets for a short time now, you can now set up web sockets really easily with Socket.io. All you have to do is the following

  1. npm install socket.io-client
  2. first import react-native
  3. assign window.navigator.userAgent = 'react-native';
  4. import socket.io-client/socket.io
  5. in your constructor assign this.socket = io('localhost:3001', {jsonp: false});

So in all it should look like this after npm installing socket.io-client:

import React from 'react-native';

// ... [other imports]

import './UserAgent';

import io from 'socket.io-client/socket.io';

export default class App extends Component {
  constructor(props) {
    super(props);
    this.socket = io('localhost:3001', {jsonp: false});
  }

  // now you can use sockets with this.socket.io(...)
  // or any other functionality within socket.io!

  ...
}

and then in 'UserAgent.js':

window.navigator.userAgent = 'react-native';

Note: because ES6 module imports are hoisted, we can't make the userAgent assignment in the same file as the react-native and socket.io imports, hence the separate module.

EDIT:

The above solution should work, but in the case it doesn't try create a separate socketConfig.js file. In there import anything that is needed, including const io = require('socket.io-client/socket.io'); and having window.navigator.userAgent = 'react-native'; BEFORE requiring socket.io-client. Then you can connect your socket there and have all listeners in one place. Then actions or functions can be imported into the config file and execute when a listener receives data.

Solution 2

Now, if you want to use socket.io in your RN app, you must use this code:

if (!window.location) {
    // App is running in simulator
    window.navigator.userAgent = 'ReactNative';
}

// This must be below your `window.navigator` hack above
const io = require('socket.io-client/socket.io');
const socket = io('http://chat.feathersjs.com', {
  transports: ['websocket'] // you need to explicitly tell it to use websockets
});

socket.on('connect', () => {
  console.log('connected!');
});

Big thanks for Eric Kryski.

Solution 3

Short of a polyfill for the WebSocket API, you can create a native module that makes use of web-sockets and send events to Javascript using eventDispatcher.

On the Javascript side, you would subscribe to these events using DeviceEventEmitter.addListener.

For more information on using native modules, see the react-native doc on the topic

Solution 4

import { io } from 'socket.io-client'
const socket = io(`${SOCKET_URL}:${SOCKET_PORT}`)

Important! SOCKET_URL should be your local IP address, not localhost or 127.0.0.1.

To check your local IP:
Mac / Linux: run ifconfig in terminal
Windows: run ipconfig --all in shell

Should be something like: const socket = io('http://10.0.1.6:3000', {transports: ['websocket']})

Solution 5

Edit Feb 2016: React Native now supports Web Sockets so some of this advice is invalid.

You've misinterpreted the Github issue I'm afraid. In it, aackerman says:

For this specific case you'll likely want to use the fetch API which is provided by the environment.

He doesn't say that you should use the fetch API to grab remote JS modules. What he's suggesting is that the fetch API be used in place of the built-in Node.JS request module, which isn't available in React Native.

Let's look at your code:

socketScript = response._bodyText;
var socket = socketScript.io();

Think about this for a second - socketScript isn't a JavaScript object, it's a string - therefore how can you call the io method on it?

What you'd really need to do is parse _bodyText before using it (in a browser you could use eval), but then you'd still have the problem that while React Native has a polyfill for XHR and the fetch API, it doesn't yet have one for the WebSocket API. Unless I'm mistaken, this means you're stuck.

I suggest opening a Github issue to request a WebSocket API polyfill and ask for the thoughts of the community. Someone might have a workaround.

Solution 6

Although you can use socket.io-client lib, the community is complaining about compatibility issues with most versions (I did experience some). It works, but now I'm afraid to upgrade the lib because I need to verify the compatibility of the next version to my server's version and react-native's version!

It seems that a lot of people miss react's own implementation of Websockets! I really recommend you use this instead of socket.io-client. It is very similar in usage:

var ws = new WebSocket('ws://host.com/path');
ws.onopen = () => {  // connection opened  ws.send('something'); // send a message};
ws.onmessage = (e) => {  // a message was received  console.log(e.data);};
ws.onerror = (e) => {  // an error occurred  console.log(e.message);};
ws.onclose = (e) => {  // connection closed  console.log(e.code, e.reason);};

Solution 7

Finally found it.

Client

import { io } from "socket.io-client/build/index"
io("ws://<LOCAL HOME NETWORK IP>:<PORT ON SERVER>")

Server

import express from "express"
import http from "http"
import * as SocketIO from "socket.io"

const app = express()
const server = new http.Server(app)
const io = new SocketIO.Server(server)
const port = 8000
io.on("connection", socket => {
   console.log("CONNECTIONS")
}

Solution 8

may be this will through error

import io from "socket.io-client/socket.io"

Then just add below line....

import io from "socket.io-client/dist/socket.io";

then in componenDidMount or useEffect function just add below line.Never use it under constructor of class component.

 var socket = io("https://localhost.com:3000", { jsonp: false });
        // client-side
        socket.on("chat_message", (msg) => {
            console.log(msg);
        });

Solution 9

2022 Answer

In 2022 you can easily just use the latest version of socket.io-client with React Native.

npm install socket.io-client

import io from 'socket.io-client';

Right now there isn't a good hook based socketIO libary that I've been able to make work with RN but it's pretty straightforward to roll out your own custom hook depending on your needs. IE

function useWebsocket(url) {
    const [connected, setConnected] = useState(false);
    const [socket, setSocket] = useState(null);
    useEffect(()=>{
        const newSocket = io(url);
        newSocket.on('connect', ()=>setConnected(true));
        newSocket.on('disconnect', ()=>setConnected(false));
        setSocket(newSocket);
    }, [])
    return {
        connected,
        socket,
    }
}

Something like this can get you started. This would open a socket for each component that calls the hook, which can work well if you just need one component with one connection. Sharing the connection across components gets a little more hairy but it isn't too bad.

The connected state is really useful for letting your user know the status of connections and stuff like that.

But yeah, point is you can just install it and use it in your component. Don't use it in the body of your functional components