reactjsreduxredux-saga

Where to store Class instance for reusability in Redux?


I am trying to implement a messaging library Chatkit by Pusher in my React/Redux/redux saga app and I'm new to Redux. The code for connecting to chatkit looks like this:

const chatManager = new ChatManager({
    instanceLocator: 'v1:us1:80215247-1df3-4956-8ba8-9744ffd12161',
    userId: 'sarah',
    tokenProvider: new TokenProvider({ url: 'your.auth.url' })
})

chatManager.connect()
    .then(currentUser => {
        console.log('Successful connection', currentUser)
    })
    .catch(err => {
        console.log('Error on connection', err)
    })

I need to store the chatManager and currentUser objects, (which are instances of complex classes with functions atttached) globally so that I can use them again later to join rooms, subscribe to events etc.

My first thought was that I should store it in Redux as that's now my "global store" but I then realised that it probably won't work as it won't be the original object I get back out of the store, it'll presumably a clone. I then read that apparently only plain objects are supposed to be stored in Redux.

So, where are things that aren't plain objects but do need to be stored globally? I don't want to put them on the window object as I may convert this to a React Native app and it seems messy anyway.


Solution

  • Per the Redux FAQ entry on storing "connection"-type objects:

    Middleware are the right place for persistent connections like websockets in a Redux app, for several reasons:

    • Middleware exist for the lifetime of the application
    • Like with the store itself, you probably only need a single instance of a given connection that the whole app can use
    • Middleware can see all dispatched actions and dispatch actions themselves. This means a middleware can take dispatched actions and turn those into messages sent over the websocket, and dispatch new actions when a message is received over the websocket.
    • A websocket connection instance isn't serializable, so it doesn't belong in the store state itself​

    edit

    Here's my "sample socket middleware" example, updated to delay creating the socket until it sees a login action:

    const createMySocketMiddleware = (url) => {
        let socket;
    
        return storeAPI => next => action => {
            switch(action.type) {
                case "LOGIN" : {
                    socket = createMyWebsocket(url);
    
                    socket.on("message", (message) => {
                        storeAPI.dispatch({
                            type : "SOCKET_MESSAGE_RECEIVED",
                            payload : message
                        });
                    });
                    break;
                }
                case "SEND_WEBSOCKET_MESSAGE": {
                    socket.send(action.payload);
                    return;
                }
            }
    
            return next(action);
        }
    }