Is it possible to combine React Native with socket.io
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
- npm install socket.io-client
- first import react-native
- assign
window.navigator.userAgent = 'react-native';
- import socket.io-client/socket.io
- 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.
React Native, NodeJS, Socket.io
after many attempts i managed to get it working thanks to Srikanth's answer on this Creating a private chat between a key using a node.js and socket.io.
CLIENT
useEffect(() => {
const newsocket =io.connect("http://192.168.1.103:9000")
setMessages(message.Messages)
newsocket.on('connect', msg => {
console.log(`user: ${user.id} has joined conversation ${message.id}`)
setSocket(newsocket)
newsocket.emit('subscribe', message.id);
});
newsocket.on("send_message", (msg) => {
console.log("this is the chat messages:", msg);
setMessages(messages => messages.concat(msg))
});
return(()=>newsocket.close());
}, []);
const onSend = (ConversationId,senderId,receiverId,message) => {
console.log("sent")
const to = (user.id===route.params.message.user1?
route.params.message.user2:route.params.message.user1)
socket.emit('message', { to: to, from: user.id, message,ConversationId });
setText("")
messagesApi.sendMessage({ConversationId,senderId,receiverId,message});
};
SERVER
io.on('connection',(socket)=>{
console.log('User '+socket.id+' connected')
socket.on('subscribe', (room)=> {
console.log('joining room', room);
socket.join(room);
});
socket.on('message', (data) => {
console.log(data)
console.log('sending room post',data.ConversationId)
io.sockets.in(data.ConversationId).emit('send_message', { message:
data.message, receiverId:
data.to,senderId:data.from,conversationId:data.ConversationId });
})
})
I have read many comments that suggested not to use rooms in 1-1 private messaging but this was the only possible way of getting it to work.
Combining socket.io with a redux reducer
You can emit the new message from the node server like.
let msg = {message: 'Actual Message', sender: 'Name of sender'}
io.on('connection', socket => {
socket.on('chat message', msg => {
io.emit('chat message', msg);
});
});
Then in you chat component listen the chat message event.
componentDidMount() {
this.socket = io("http://server.address:port");
this.socket.on("chat message", msg => {
this.props.onMessageReceivedAction (msg); // import action and will execute on message received event
});
});
Then add the action :
export const onMessageReceivedAction = (params) => {
return {type: MESSAGE_RECEIVED, payload:msg};
};
Add message received reducer case to your reducer
case MESSAGE_RECEIVED:
let newMessage = {
sender: payload.sender,
message: payload.message
};
return { ...state, chats: state.chats.push(newMessage) };
This will return the new state and the component props mapped to the above state will get re-rendered.
Hope this will answer your question.
socket.io client receiving multiple calls to same event in react native
I think you can try adding socket event handler only when your Chat component mounted.
In functional component, you can use React.useEffect().
refer to below
React.useEffect(() => {
socket.on("newMessage", ({ msgBody, sender }) => {
setData((oldMessages) => [...oldMessages, msgBody]);
});
},[]);
Socket.io opening multiple connections with React-Native
So here I come with an answer. I'll try to leave an answer as the one I'd like to find. A sort of tutorial about how to include Socket.io in React-native. Please, if you know a better solution, write it down.
As I wrote, the problem is that socket in React-Native is global and in this way I wrong implementation is just more evident.
First of all, I initialized socket in the wrong place. The correct place I found is in App.js, where the router is. I remove some code for clarity.
// important to access the context of React
import PropTypes from 'prop-types';
// Library used to save encrypted token
import * as Keychain from 'react-native-keychain';
// url to address
import { BASIC_WS_URL } from '../../api';// library to encrypt and decrypt data
const io = require('socket.io-client/dist/socket.io');
prepare this function within contructor and componentDidMount:
state = {}
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
keichain is a promise, so it won't work in componentDidMount. To make it work, you have to do the following, so every step will wait for the previous to be done:
async componentWillMount() {
const response = await Keychain.getGenericPassword();
const websocket = await io(BASIC_WS_URL, {
jsonp: false,
// forceNew:true,
transports: ['websocket'],
query: {
token: response.password
}
});
await websocket.connect();
await websocket.on('connect', (socket) => {
console.log('Sono -> connesso!');
});
await websocket.on('reconnect', (socket) => {
console.log('Sono riconnesso!');
});
await websocket.on('disconnect', (socket) => {
console.log('Sono disconnesso!');
});
await websocket.on('error', (error) => {
console.log(error);
});
// a function imported containing all the events (passing store retrieved from context declare at the bottom)
await socketEvents(websocket, this.context.store);
// then save the socket in the state, because at this point the component will be already rendered and this.socket would be not effective
await this.setStateAsync({websocket: websocket});
}
Remember to remove the console.logs then. They are there just for verification. Right after this, remeber to disconnect when unmounting:
componentWillUnmount() {
this.state.websocket.disconnect()
}
And right after this, save the socket in the context:
getChildContext() {
return {websocket: this.state.websocket};
}
Remeber to declare the context at the bottom of the component:
App.childContextTypes = {
websocket: PropTypes.object
}
// access context.type to get the store to pass to socket.io initialization
App.contextTypes = {
store: PropTypes.object
}
So, final result is this:
...
// important to access the context of React
import PropTypes from 'prop-types';
// Library used to save encrypted token
import * as Keychain from 'react-native-keychain';
// url to address
import { BASIC_WS_URL } from '../../api';// library to encrypt and decrypt data
const io = require('socket.io-client/dist/socket.io');
...
class App extends Component {
constructor() {
super();
...
}
state = {}
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
// set the function as asynchronous
async componentWillMount() {
//retrieve the token to authorize the calls
const response = await Keychain.getGenericPassword();
// initialize the socket connection with the passwordToken (wait for it)
const websocket = await io(BASIC_WS_URL, {
jsonp: false,
// forceNew:true,
transports: ['websocket'], // you need to explicitly tell it to use websockets
query: {
token: response.password
}
});
// connect to socket (ask for waiting for the previous initialization)
await websocket.connect();
await websocket.on('connect', (socket) => {
console.log('Sono -> connesso!');
});
await websocket.on('reconnect', (socket) => {
console.log('Sono riconnesso!');
});
await websocket.on('disconnect', (socket) => {
console.log('Sono disconnesso!');
});
await websocket.on('error', (error) => {
console.log(error);
});
// a function imported containing all the events
await socketEvents(websocket, this.context.store);
await this.setStateAsync({websocket: websocket});
}
componentWillUnmount() {
this.state.websocket.disconnect()
}
getChildContext() {
return {websocket: this.state.websocket};
}
render() {
return (
... // here goes the router
);
}
}
App.childContextTypes = {
websocket: PropTypes.object
}
// access context.type to get the store to pass to socket.io initialization
App.contextTypes = {
store: PropTypes.object
}
export default App;
then, in any page/container, you can do like this. -> declare the context in the bottom of the component:
Main.contextTypes = {
websocket: PropTypes.object
}
And when you dispatch an action, you will be able then to emit:
this.props.dispatch(loadNotif(this.context.websocket));
In the action creator, you will emit like this:
exports.loadNotif = (websocket) => {
return function (dispatch) {
// send request to server
websocket.emit('action', {par: 'blablabla'});
};
};
I hope it will help somebody.
React Native Socket.io How to Connect to Local Node Server from Device
Solved by tunneling localhost:3000
via ngrok and allowing an exception domain.
In your info.plist
you need the following under App Transport Security Settings
On a mac command line run
brew cask install ngrok
ngrok http 3000
Then grab the outputted ngrok.io URL and use it in your io.connect()
call and you should be set.
Related Topics
Uitableview: How to Disable Selection for Some Rows But Not Others
Navigationlink Works Only for Once
How to Route Audio to Speaker Without Using Audiosessionsetproperty
Nscamerausagedescription in iOS 10.0 Runtime Crash
How Can a Web Application Send Push Notifications to iOS Devices
Failed to Obtain a Cell from Its Datasource
Restrict to Certain iOS Target Devices for App Store Submission
Counting the Number of Lines in a Uitextview, Lines Wrapped by Frame Size
Is Silent Remote Notifications Possible If User Has Disabled Push for the App
iOS 8 Enabled Device Not Receiving Push Notifications After Code Update
Programmatically Creating an Expanding Uitableviewcell
Uitableview - Change Section Header Color
Required Ipv6 Compatibility - iOS App Rejected by Apple
Unique Identification of iOS Device for iOS 7.0 and Above