Coder Social home page Coder Social logo

redux-socket.io's Introduction

redux-socket.io

An opinionated connector between socket.io and redux.

Philosophy

Socket.io client->server messages should be sent by dispatching actions to redux's store, where the action is the payload.
Socket.io server->client messages should be dispatched as actions when received.

How to use

Installation

npm install --save redux-socket.io

Example usage

This will create a middleware that sends actions to the server when the action type starts with "server/". When the socket.io socket receives a message of type 'action', it will dispatch the action to the store.

The result of running this code from the client is a request to the server and a response from the server, both of which go through the redux store's dispatch method.

Client side:

import { createStore, applyMiddleware } from 'redux';
import createSocketIoMiddleware from 'redux-socket.io';
import io from 'socket.io-client';
let socket = io('http://localhost:3000');
let socketIoMiddleware = createSocketIoMiddleware(socket, "server/");
function reducer(state = {}, action){
  switch(action.type){
    case 'message':
      return Object.assign({}, {message:action.data});
    default:
      return state;
  }
}
let store = applyMiddleware(socketIoMiddleware)(createStore)(reducer);
store.subscribe(()=>{
  console.log('new client state', store.getState());
});
store.dispatch({type:'server/hello', data:'Hello!'});

Server side:

var http = require('http');
var server = http.createServer();
var socket_io = require('socket.io');
server.listen(3000);
var io = socket_io();
io.attach(server);
io.on('connection', function(socket){
  console.log("Socket connected: " + socket.id);
  socket.on('action', (action) => {
    if(action.type === 'server/hello'){
      console.log('Got hello data!', action.data);
      socket.emit('action', {type:'message', data:'good day!'});
    }
  });
});

Allowed criteria for action matching

When you create this middleware, you can configure how it detects that a given action should be sent to socket.io. This is done with the second parameter to createSocketIoMiddleware.

You can pass either a prefix string that will be matched against the action.type:

let socketIoMiddleware = createSocketIoMiddleware(socket, 'server/');

An array of strings that will be used as allowed prefixes:

let socketIoMiddleware = createSocketIoMiddleware(socket, [ 'post/', 'get/' ]);

Or a function that returns a truthy value if the action should be sent to socket.io:

let socketIoMiddleware = createSocketIoMiddleware(socket, (type, action) => action.io);

Advanced usage

The default behavior is an "optimistic" update mode, where if an action matches the criteria you provided when you created the socket.io middleware, the middleware calls socket.emit('action', action) and then passes the action to the next middleware in the chain.

If you want to change this behavior, you can provide your own execute function that allows you to decide what to do with the action that matched your criteria.

You do this by providing a function (action, emit, next, dispatch) as the execute property of the third parameter of createSocketIoMiddleware

Example execute functions:

This is equivalent to the default execute function, so this is what will happen if you don't override it. Use something like this if you want optimistic updates of your state, where the action you dispatch goes both to the server and to the redux reducers.

import createSocketIoMiddleware from 'redux-socket.io';
function optimisticExecute(action, emit, next, dispatch) {
  emit('action', action);
  next(action);
}
let socketIoMiddleware = createSocketIoMiddleware(socket, "server/", { execute: optimisticExecute });

Here's a function that would make the middleware swallow all the actions that matched the criteria and not allow them to continue down the middleware chain to the reducers. This is easily used to make "pessimistic" updates of your state, by having the server respond by sending back an action type of the same type it was sent.

import createSocketIoMiddleware from 'redux-socket.io';
function pessimisticExecute(action, emit, next, dispatch) {
  emit('action', action);
}
let socketIoMiddleware = createSocketIoMiddleware(socket, "server/", { execute: pessimisticExecute });

Here's a function that would make the middleware dispatch an alternate action that could be used in a scenario where you want the optimistic updates to be very explicit. Here you would have actions of type server/<actionName> sent to the server, and also have another action optimistic/<actionName> dispatched as well with the same content.

import createSocketIoMiddleware from 'redux-socket.io';
function optimisticExecute(action, emit, next, dispatch) {
  emit('action', action);
  const optimisticAction = {
    ...action,
    type: 'optimistic/' action.type.split('/')[1];
  }
  dispatch(optimisticAction);
}
let socketIoMiddleware = createSocketIoMiddleware(socket, "server/", { execute: optimisticExecute });

MIT License

Copyright (c) 2015-2016 Ian Taylor

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

redux-socket.io's People

Contributors

1egoman avatar andrewbroz avatar itaylor avatar maxdow avatar tylercrosse avatar zaurbek avatar zcei avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redux-socket.io's Issues

how to manage the connection?

Hi, How to disable listening to a specific event. Or how to close/reopen the connection? Where is the normal documentation?

, readme

In Readme:

socket.emit('action', {type:'message' data:'good day!'});

need ",":

socket.emit('action', {type:'message', data:'good day!'});

How do I get access to my jwt first and then connect to socketiomiddleware?

console.ignoredYellowBox = ["Remote debugger"];
import { YellowBox } from "react-native";
YellowBox.ignoreWarnings([
  "Unrecognized WebSocket connection option(s) `agent`, `perMessageDeflate`, `pfx`, `key`, `passphrase`, `cert`, `ca`, `ciphers`, `rejectUnauthorized`. Did you mean to put these under `headers`?"
]);
import React, { useState } from "react";
import { StyleSheet} from "react-native";
import * as Font from "expo-font";
import { AppLoading } from "expo";
import AppNavigation from "./navigation/AppNavigator";
import { createStore, applyMiddleware, combineReducers } from "redux";
import { Provider } from "react-redux";
import { setNavigator } from './navigationRef';
import ReduxThunk from 'redux-thunk';
import createSocketIoMiddleware from "redux-socket.io";
import {userReducer} from "./reducers/userReducer";
import {chatReducer} from "./reducers/chatReducer";

const store = createStore(combineReducers({userReducer, chatReducer}), applyMiddleware(ReduxThunk, socketIoMiddleware));

 let currentValue = store.getState().userReducer.token;
 const socket = io.connect('http://81.89.193.99:3001/chat', {
     query: {token: token} })
console.log("CuurentValue", currentValue);
const socketIoMiddleware = createSocketIoMiddleware(socket, "chat:");
const fetchFont = () => {
  return Font.loadAsync({ 
    "raleway-bold": require("./assets/fonts/Raleway-Bold.ttf"),
    ralewayBold: require("./assets/fonts/Raleway-Bold.ttf"),
    "roboto-regular": require("./assets/fonts/Roboto-Regular.ttf"),
    robotoRegular: require("./assets/fonts/Roboto-Regular.ttf"),
    Roboto_medium: require("./assets/fonts/Roboto-Regular.ttf"),
  });
};

export default function App() {
  const [fontLoaded, setFontLoaded] = useState(false);
  if (!fontLoaded) {
    return (
      <AppLoading
        startAsync={fetchFont}
        onFinish={() => setFontLoaded(true)}
        onError={(err) => {
          console.log(err);
        }}
      />
    );
  }
  return (
    <Provider store={store}>
      <AppNavigation ref = {(navigator) => {setNavigator(navigator)}} />
    </Provider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

Socket.emit loses it's closure

With the new update I'm getting and TypeError on the emit.

uncaught at rootSaga 
 at connectionSaga 
 TypeError: Cannot read property 'push' of undefined
    at Socket.emit (http://localhost:3002/dist/main.js:28970:22)
    at defaultExecute (http://localhost:3002/dist/main.js:62708:7)
    at type (http://localhost:3002/dist/main.js:62679:13)
    at dispatch (http://localhost:3002/dist/main.js:62761:20)
    at http://localhost:3002/dist/main.js:27599:54
    at Function.asap.flush (http://localhost:3002/dist/main.js:62338:7)
    at asap (http://localhost:3002/dist/main.js:62326:12)
    at runPutEffect (http://localhost:3002/dist/main.js:27596:26)
    at runEffect (http://localhost:3002/dist/main.js:27550:222)
    at next (http://localhost:3002/dist/main.js:27434:11)

This on socket.io 1.5.0

Basically the defaultExecute function is eroding the closure on the emit. Therefore when this.sendBuffer.push is called this is actually pointing to the window in chrome, thus sendBuffer is undefined and we get glorious crashing.

I've done a simple fix, and will be in a pull request. You could also make default excute a lambda, which 'should' preserve the closure but I haven't tested this.

incompatibility with middlewares based on function

I have the following case:

  1. I'm using redux-thunk
  2. I have action creators that return functions insted of plain objects

redux-socket.io doesn't have this case into acount, so I get and error in:

function evaluate(action, option) {
    var type = action.type;

    var matched = false;
    if (typeof option === 'function') {
      // Test function
      matched = option(type, action);
    } else if (typeof option === 'string') {
      // String prefix
      matched = type.indexOf(option) === 0;
    } else if (Array.isArray(option)) {
      // Array of types
      matched = option.some(function (item) {
        return type.indexOf(item) === 0;
      });
    }
    return matched;
  }

Uncaught TypeError: Cannot read property 'indexOf' of undefined

Because when in the first line of the function var type = action.type; there is not type.

bindActionCreators and socket.on('action', ...)

When the following is run on the client

store.dispatch({type:'server/hello', data:'Hello!'});

The server successfully receives the message.

socket.on('action', action => {
    console.log('Got hello data!', action.data);
});

All action creators bound to my component using redux's bindActionCreators are dispatched on the client, but not received by the server. Am I missing something?

Asynchronously loaded client script

Hi there. First I would like say thank you for great plugin.

I like the concept of createSocketIoMiddleware, but would like to load socket.io client lib and initialise socket connection only when user visits some specific part of an SPA.

So it is not possible to initialise createSocketIoMiddleware in advance, because io library is not present yet. Do you have any suggestion how to handle this situation? Thanks

Server->client messages are getting echoed back to the websocket server

I am trying to send a message from a server to a client and to have an action dispatched client-side.

The action is getting dispatched successfully, but the client echos back the message it received to the server, resulting in unnecessary server traffic. Is there any way to disable this server->client->server echoing behavior?

How use it with react-hot-loader?

I cannot use this component with webpack-dev-server and react-hot-loader(((
Why I get this error and how I can build properly my app?

I use babel and webpack2. My components:

"redux-socket.io": "^1.4.0",
"socket.io": "^2.0.1",

My errors:

ERROR in .//socket.io-client//debug/browser.js
Module build failed: Error: ENOENT: no such file or directory, open '/mnt/develop/www/domains/frontend.webqual.lo/node_modules/socket.io-client/node_modules/debug/browser.js'
@ ./~/socket.io-client/lib/index.js 9:12-28
@ ./app/store.js
@ ./app/index.js
@ multi react-hot-loader/patch webpack-dev-server/client?http://localhost:3000 webpack/hot/only-dev-server vendor.js index.js

ERROR
in .//engine.io-client//debug/browser.js
Module build failed: Error: ENOENT: no such file or directory, open '/mnt/develop/www/domains/frontend.webqual.lo/node_modules/engine.io-client/node_modules/debug/browser.js'
@ .//engine.io-client/lib/socket.js 7:12-28
@ ./
/engine.io-client/lib/index.js
@ .//engine.io-client/index.js
@ ./
/socket.io-client/lib/manager.js
@ ./~/socket.io-client/lib/index.js
@ ./app/store.js
@ ./app/index.js
@ multi react-hot-loader/patch webpack-dev-server/client?http://localhost:3000 webpack/hot/only-dev-server vendor.js index.js

server reboot, socket is expired. real time is broken

Hi, this awesome kit make my realtime chat and chatlist realtime update possible.
however, when the server get rebooted. the users must get connected again to keep the real time chatlist updated. otherwise, the realtime go broken.
how can I solve this problem?
Thank you

Getting error - `createSocketIoMiddleware is not a function` when using node's require()

Hey man!

First off, thanks for the awesome work.

Onto my issue- I have a use case where I need to use redux-socket.io on the server side, but I'm immediately hitting a brick wall.

Here's my store initialization on the server...

//** Redux Stuff **//
const createStore = require('redux').createStore;
const thunk = require('redux-thunk');
const reducers = require('../server-reducers');

const secrets = require('../secrets.json');

const createSocketIoMiddleware = require('redux-socket.io');

// We need the socket object as a parameter here because socket connects...
// before the store is initialized.
module.exports = function createServerStore(io) {

  const socketIoMiddleware = createSocketIoMiddleware( //** Boop! '... is not a function'
    io,
    ['server/', 'HiBoards::', 'HiSocket::']
  );

  io.emit('Initialised Store for Connection', { data: 'yo' });
  console.log('Store Initialized');

  return createStore(
    reducers,
    compose(
      applyMiddleware(
        thunk,
        socketIoMiddleware
      )
    )
  );
}

Not sure why this is happening on the backend but the front-end is working fine, might this be an issue with require() vs import?

Versions below

nodejs: 6.9.2
npm: 3.10.8
createSocketIoMiddleware: 1.3.1

Handling authentication

Hi,
How to handle authentication using redux-socket.io?
For example, I'm using JWT with socketio-jwt but socket.io requires to configure the token at connection time, eg.

const socket = io.connect(API_ROOT, {
  'query': 'token=' + jwt_token
});

As jwt_token is retrieved at login time, I store it in the redux's store but redux-socket.io need to be configured before the store is instantiated.

How can I solve this vicious circle?

Detecting client side disconnect

Hello I've been using your redux-socketio connector and I love it, only problem is I have no idea how to detect a disconnect from the client side. Any help is much appreciated. Thanks!

Dynamically listen to channels when user logs in

Is there a way, I can dynamically listen to channels after the user logs in?
I do not want all the clients to listen to all the events. It would be nice if there were more details in the readme.

Call next only on when not emitting

Hej,

loving the simplicity of your package, it was really easy for me to spot the proposed code change ๐Ÿ‘

The issue is, that when your middleware is matching an action type, the next handler still gets called.
Is this intended behavior?

I'd like to have at least an configuration option, whether it should act like an interceptor or passthrough, so that the client-side "replay" that redux offers still works.
Currently it would emit all server actions again, what in some use-cases might be wanted, but in others may not.

I currently face such situation and thus would offer a PR in case that's something you're up to.

(it's a bit redux-thunk style, where you can whether swallow an action or emit something)

How can I changed the "catchable" server emit EVENT NAME in the plugin?

It seems the socket.io server emit to client 's event name must be "action", like this:
Server:
socket.emit('action', {type:'message', data:'good day!'});

or the plugin can't catch the server event and reduxed it

How can I changed the "catchable" EVENT NAME ?
The { execute: optimisticExecute } can only changed the client emit event name

Ask for hurry, I'm ready to use the plugin in the production!

Async middleware loading

Hi, I am working on an universal/isomorphic redux app and loading socket.io after the rest of the app is loaded, on componentDidMount.
Having issues for using this middleware before socket.io is loaded, I was asking me for your example: isn't slow to wait for socket.io beeing loaded to create the redux store with the middlewares?

Thanks for sharing this!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.