Skip to content

Instantly share code, notes, and snippets.

@roman01la
Created February 26, 2016 13:48
Show Gist options
  • Save roman01la/0c327a8a8f3c03435d0f to your computer and use it in GitHub Desktop.
Save roman01la/0c327a8a8f3c03435d0f to your computer and use it in GitHub Desktop.
Redux WebSockets recipe
const MODULE_NAME = 'base-app/events/'
export const CONNECT_WS = MODULE_NAME.concat('CONNECT_WS')
export const DISCONNECT_WS = MODULE_NAME.concat('DISCONNECT_WS')
export const SUBSCRIBE_WS = MODULE_NAME.concat('SUBSCRIBE_WS')
export const UNSUBSCRIBE_WS = MODULE_NAME.concat('UNSUBSCRIBE_WS')
export const EMIT_WS = MODULE_NAME.concat('EMIT_WS')
export const NEW_EVENT = MODULE_NAME.concat('NEW_EVENT')
export const ADD_EVENT = MODULE_NAME.concat('ADD_EVENT')
export function connect(socketID) {
return (dispatch) => {
return dispatch({
type: CONNECT_WS,
socketID
})
}
}
export function disconnect(socketID) {
return (dispatch) => {
return dispatch({
type: DISCONNECT_WS,
socketID
})
}
}
export function subscribe(socketID, event) {
return (dispatch) => {
return dispatch({
type: SUBSCRIBE_WS,
socketID,
event
})
}
}
export function unsubscribe(socketID, event) {
return (dispatch) => {
return dispatch({
type: UNSUBSCRIBE_WS,
socketID,
event
})
}
}
export function emit(socketID, data) {
return (dispatch) => {
return dispatch({
type: EMIT_WS,
socketID,
data
})
}
}
import React, { PropTypes, Component } from 'react'
import { connect as connectState } from 'react-redux'
import { bindActionCreators } from 'redux'
import { connect, disconnect, subscribe, unsubscribe } from '../modules/events'
import { NEW_EVENT } from '../modules/events'
const devTools = __DEV__ ? React.createFactory(require('../containers/dev-tools').default) : () => null
class Events extends Component {
render() {
return (
<div>
<button onClick={() => this.props.connect('events')}>Connect to events socket</button>
<button onClick={() => this.props.disconnect('events')}>Disconnect from events socket</button>
<button onClick={() => this.props.subscribe('events', NEW_EVENT)}>Subscribe to a new events</button>
<button onClick={() => this.props.unsubscribe('events', NEW_EVENT)}>Unsubscribe from a new events</button>
<ul>
{this.props.events.map(({ message }) => <li key={message}>{message}</li>)}
</ul>
{devTools()}
</div>
)
}
}
function mapStateToProps(state) {
return {
events: state.events
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ connect, disconnect, subscribe, unsubscribe }, dispatch)
}
Events.propTypes = {
events: PropTypes.array.isRequired,
connect: PropTypes.func.isRequired,
disconnect: PropTypes.func.isRequired,
subscribe: PropTypes.func.isRequired,
unsubscribe: PropTypes.func.isRequired
}
export default connectState(mapStateToProps, mapDispatchToProps)(Events)
import io from 'socket.io-client'
import { CONNECT_WS, DISCONNECT_WS, SUBSCRIBE_WS, UNSUBSCRIBE_WS, EMIT_WS } from '../modules/events'
let config = {}
const sockets = {}
function connect(url, socketID) {
sockets[socketID] = {
connection: io(url)
}
}
function disconnect(socketID) {
sockets[socketID].connection.disconnect()
sockets[socketID] = undefined
}
function subscribe(socketID, event, dispatch) {
const listener = (data) => dispatch({ type: event, data })
sockets[socketID].listener = listener
sockets[socketID].connection.on(event, listener)
}
function unsubscribe(socketID, event) {
sockets[socketID].connection.removeListener(event, sockets[socketID].listener)
}
function emit(socketID, data) {
sockets[socketID].emit(data)
}
// usage: applyMiddleware(thunk, api, createWSMiddleware({ events: 'http://localhost:3000' }))
export default function createWSMiddleware(wsConfig) {
config = wsConfig
return store => next => action => {
const { type, socketID, event, data } = action
if (type === CONNECT_WS) {
connect(config[socketID], socketID)
}
if (type === DISCONNECT_WS) {
disconnect(socketID)
}
if (type === SUBSCRIBE_WS) {
subscribe(socketID, event, store.dispatch)
}
if (type === UNSUBSCRIBE_WS) {
unsubscribe(socketID, event)
}
if (type === EMIT_WS) {
emit(socketID, data)
}
return next(action)
}
}
import * as actions from './actions'
export * from './actions'
const initialState = []
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case actions.NEW_EVENT:
return state.concat(action.data)
default:
return state
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment