reactjsreact-reduxphoenix-frameworkphoenix-channels

Where to store socket connection in react-redux?


Tried two ways:

  1. call connectToServer() from action creator within Starter component in componentDidMount(); and dispatch like this:

    let socket = new Socket('ws://address/socket');
    
    socket.connect();
    
    dispatch({
      type: Constants.SESSION_SAVE_SOCKET,
      socket: socket,
    });
    
    const lobbyChannel = socket.channel('lobby');
    
    lobbyChannel.join()
    .receive('ok', () => {         
      dispatch({
        type: Constants.SESSION_LOBBYCHANNEL_RECEIVE_OK,
      });
    
      dispatch({
        type: Constants.SESSION_SAVE_LOBBYCHANNEL,
        lobbyChannel: lobbyChannel,
      });
    
    }).receive('error', (payload) => {            
        dispatch({
          type: Constants.SESSION_LOBBYCHANNEL_RECEIVE_ERROR,
        });
    });
    

Next I receive state by redux's mapStateToProps connect. The result is component is called four times and props are empty at the result.

  1. put all logic into the reducer, but the result is: component is rendered with empty props (undefined properties) and moment after I see in console logs that connection is established, but component is already rendered.

How to deal with such a issue? Thanks for any suggestions.


Solution

  • The way I found that works is to setup your own middleware for the socket like so.

    import {createStore, applyMiddleware} from 'redux';
    import startWs, {wsMiddleware} from './ws.api';
    
    function handleData(state = {data1: {}}, action) {
      switch (action.type) {
        case 'ApiGotData': return Object.assign({}, state, {data1: action.data});
        default: return state;
      }
    }
    
    const store = createStore(handleData, applyMiddleware(wsMiddleware));
    
    startWs(store);
    
    export default store;

    import * as Actions from './Actions';
    
    var socket = null;
    
    const newData = {
      'React version': '15',
      'Project': 'Redux with socket.io',
      'currentDateTime': new Date().toLocaleString()
    };
    
    export function wsMiddleware() {
      return (next) => (action) => {
        if (socket && action.type === 'ApiGetData') {
          console.log('ApiGetData');
          socket.emit('client:GetData', {});
        } else if (socket && action.type === 'ApiSetData') {
          console.log('ApiSetData');
          socket.emit('client:SetData', action.data);
        }
    
        return next(action);
      };
    }
    
    export default function (store) {
      socket = new io();
    
      socket.on('server:GetDataDone', (data) => {
        console.log('GetDataDone');
        store.dispatch(Actions.apiGotData(data));
      });
    
      socket.on('server:SetDataDone', () => {
        console.log('SetDataDone');
        store.dispatch(Actions.apiGetData());
      });
      
      store.dispatch(Actions.apiSetData(newData));
    }

    The project example is ReduxSocketIO at https://github.com/jmarkstevens/ReactPatterns.