Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dispatch action on the callback of socket.on()

So basically I got this socket, that is working correctly sending me 'new order' message.

I'm using redux, and i want to dispatch an action, than a reducer would get it and my store would be updated. but this code doesn't do anything!

socket.on('new order', (order) => {    
    return (dispatch) => {
      dispatch(socketNewOrder(order));
    }
});

And here is my action, which is located at the same file:

export const socketNewOrder = (order) => {
  return {
    type: 'SOCKET_NEW_ORDER',
    payload: order
  }
}

I also tried to call my action like this:

socket.on('new order', (order) => {    
    socketNewOrder(order));        
});

It called the action indeed, but the action wasn't 'heard' by my reducer! :(

I heard something about using middlewares, but i just can't figure out how to do it.

Could anyone explain me how to use middlewares for dispatching actions as i receive socket messages, and why my code doesn't work? Thanks and sorry for newbie questio

like image 823
Amanda Siqueira Avatar asked Feb 04 '23 16:02

Amanda Siqueira


2 Answers

This code should work for you:

export const socketNewOrder = (order) => {
  return {
    type: 'SOCKET_NEW_ORDER',
    payload: order
  }
}

const handlerFunc = (dispatch) => (order) => {
    dispatch(socketNewOrder(order));
  }
});

socket.on('event', handlerFunc(dispatch));
// make sure the stores dispatch method is within scope

Explanation

Your event handler function were correctly broken down into a series of functions. However these functions were in the wrong order.

socket.on('new order', (order) => {    
    return (dispatch) => {
      dispatch(socketNewOrder(order));
    }
});

This is the correct order of the series functions that make up your eventHandler function:

socket.on('new order', (dispatch) => {    
    return (order) => {
      dispatch(socketNewOrder(order));
    }
});

Binding a handler function to a socket in the normal way would look like.

socket.on('event', handlerFunc)

So the handler function would only be called when the event is triggered.

This won't work for us if we need to pass dispatch to the handlerFunc when it is bound before handlerFunc is called when the event is triggered.

However we can solve this through the use of a functional programming technique called currying which allows us to break the event handler handler function into a series of functions that can be called at progressively later points in time.

Currying is when you break down a function that takes multiple arguments into a series of functions that take part of the arguments.

There are two important points in time for socket events.

  1. The handler function is bound to the socket instance

  2. The handler function is called

We have access to the Redux store's dispatch method at timepoint one but not at timepoint two. Currying allows us to 'store' the dispatch method for timepoint two.

So what we can do is call a function with dispatch that returns our handlerFunction.

function handlerFunc(order){
  dispatch(socketNewOrder(order));
}

function passDispatch(dispatch){
  return handlerFunc
};

socket.on('event', passDispatch(dispatch));

So although this may look odd it results in exactly the same thing as the first example. Through currying although the event handler will be called at a later point in time we will still be able to dispatch actions as we have access to the dispatch variable.

We could use middleware to alleviate the repetition of currying our handler functions each time they are bound.

like image 85
therewillbecode Avatar answered Feb 08 '23 16:02

therewillbecode


The point is that you need to have access to dispatch in your socket event listener. Using a middleware to create actions from external event sources is a valid pattern.

const socketMiddleware = (store) => {
  // We have access to store, add socket listeners
  socket.on('new order', (order) => {    
    store.dispatch(socketNewOrder(order));
  });  

  // Actual middleware implementation just passes all
  // actions through without touching them
  return (next) => (action) => {
    next(action);
  }
}
like image 26
OlliM Avatar answered Feb 08 '23 14:02

OlliM