Search code examples
reduxcyclejs

Update source and sink dynamically in cycle js?


I'm writing a very simple app using redux-cycle, with 3 elements:

  1. A text field to input url
  2. A "connect" button to connect to a websocket end point
  3. An output panel to show incoming messages

I have written a very simple websocket driver based on this answer https://stackoverflow.com/a/42926532/842860

The wire up code is like this:

const cycleMiddleware = createCycleMiddleware();
const {makeActionDriver} = cycleMiddleware;

function main(sources) {
    return {
        ACTION: sources.WEBSOCKET // The websocket driver emits commands which are consumed by reducers
    }
}

const store = createStore(
    reducer, // Update redux state
    applyMiddleware(cycleMiddleware)
);

run(main, {
    WEBSOCKET: WSDriver("ws://localhost:3000/something"), // The initial connection string
    ACTION: makeActionDriver()
});

The question is how to make the websocket reconnect to another end point when the connect button is clicked? Do I need to modify my websocket driver to capture the event and reconnect, or does cyclejs provide a way to dynamically update the source/sink?


Solution

  • I think the appropriate method would be to, indeed, update the WSDriver to take a sink$ as input. This sink$ could contain the url you want to connect to.

    Your driver would look like this

    function WSDriver(endpoint$) {
      var activeConnection;
      return endpoint$
        .map(endpointUrl => xs.create({
          start: listener => {
           activeConnection = new WebSocket('ws://localhost:4000');
           /* no change here */
          },
          stop: () => {
            activeConnection.close();
          }
        }))
        .flatten() // it's a stream of stream, so we need to flatten it
    }
    

    On the app side you would need to change this

    run(main, {
        WEBSOCKET: WSDriver(), // < no initial connection
        ACTION: makeActionDriver()
    });
    

    And in your main

    function main(sources) {
        return {
            ACTION: sources.WEBSOCKET,
            WEBSOCKET: newConnection$.startWith("ws://localhost:3000/something")
        }
    }
    

    Given that the newConnection$ comes from the click events on the button.

    Hope it helps