Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomasgwatson/1cda6b7d611ad9e1a36c3d01bf5a3e36 to your computer and use it in GitHub Desktop.
Save thomasgwatson/1cda6b7d611ad9e1a36c3d01bf5a3e36 to your computer and use it in GitHub Desktop.
Discussion about persistent connections and React Redux
https://medium.com/lexical-labs-engineering/redux-best-practices-64d59775802e#.iiprvvekh
tom.watson-Today at 3:45 PM
Are thunks or sagas better suited to handling persistent connections? I'm about to try implementing async stuff with redux for the first time...
jokeyrhyme-Today at 4:06 PM
@tom.watson you can have a subscriber to the store publish changes to a WebSocket or something. And you could have an event listener on the socket dispatch actions to the store.
A persistent connection is really just an input/output, just like the DOM or IndexedDB are.
You might not need either thunks or sagas for that.
tom.watson-Today at 4:12 PM
@jokeyrhyme I guess I'm wondering where I put that code and instantiate it. I don't want to tack it on to my React Component...
Essentially I want a persistent connection to an elasticsearch cluster, that can generate actions (on a setInterval query) and can receive actions to trigger queries
kbsymanz-Today at 4:17 PM
@tom.watson Sounds to me like you need some sort of middleware. Many advise to start with thunks because they are simpler and to refactor to something else if ever it is needed, or add something else for complicated scenerios.
tom.watson-Today at 4:28 PM
@kbsymanz Essentially I can create a middleware thunk that can instantiate a socket client, and take actions to interact with the socket client. Like the actions.js file here? https://github.com/markerikson/redux/blob/create-faq-page/docs/advanced/AsyncActions.md#async-action-creators
GitHubmarkerikson/redux
redux - Predictable state container for JavaScript apps
Shados-Today at 4:38 PM
@tom.watson you really have roughly 3-4 options here.
at least that i like (tm)
1) you can instanciate your connection at your app startup, in the same place you create the store and stuff....and then you can open a connection, and since there you have access to the store directly, you can call store.dispatch on all the events you care about on your persistant connection (I assume you mean web sockets)
2) you can make a component that renders nothing. onComponentDidMount, create a connection. onComponentDidUnmount, close/clean it. Drop the component near the top level of your app, enjoy. Everything else will work like normal react-redux, which is nice.(edited)
3) Sagas of any kind (redux-sagas or redux-saga-rxjs for example) will work nicely, since they are persistant themselves....make sure to instanciate the connection inside an effect/stream else testing will suck.
4) you can totally just create a module that takes the store as argument and creates a socket object that dispatch actions, similar to 1), and cal that from wherever you want that has access to the store.
IMO these are in order of complexity and reverse order of testability. Choose your poison
tom.watson-Today at 4:44 PM
@Shados thanks for spoiling me with choice
Shados-Today at 4:44 PM
Spoiling or cursing?
I assume you would have prefered a "This is the obvious standard option"
sorry I could not give that
tom.watson-Today at 4:45 PM
Hahaha, I knew it wasn't going to be that easy
Shados-Today at 4:45 PM
if you're just starting and getting your feet wet, use 1)
2 is pretty trivial too
kbsymanz-Today at 4:45 PM
For what it is worth, I did #4 and it is working well to this point.
tom.watson-Today at 4:55 PM
Trying to better understand option 4: It takes the store as an arg, creates and returns a socket object (which can dispatch actions via the store that is passed in)
And it's returned and is accessible where the store is accessible because..... You've also passed it down in props? Or you've attached it as a property of the store?
@kbsymanz @Shados
Shados-Today at 4:57 PM
I meant more something like:
kbsymanz-Today at 4:58 PM
I save the store in the module with an init() function which I call on startup.
Shados-Today at 5:01 PM
let socket;
export default (store) => {
socket = new Socket();
socket.on('receive', (data) => store.dispatch({type: RECEIVE, payload: data);
}
export const sendData = (payload) {
return (dispatch) => {
dispatch({ type: SENDING });
socket.send(payload);
dispatch({ type: SENDING_COMPLETE });
}
}
(edited)
or whatever
this is high level pseudocode (and its bad)
kbsymanz-Today at 5:01 PM
@tom.watson Here is a gist of what I do (incomplete): https://gist.github.com/kbsymanz/790659fecbbbd3d524a0
Gistcomm.js
Shados-Today at 5:02 PM
but its basically the idea @kbsymanz is describing
tom.watson-Today at 5:12 PM
@Shados @kbsymanz That helps me make sense of the module internals for the socket. I'm still a little confused about how to hook it into the rest of the app. So I call Comm(store) on app startup (when you are initing the store etc). Now it's hanging around to passively receive data over the socket. But if I want to trigger queries (over a duplex connection), should I pass it down and around so it can be triggered by user events? Does my explanation of my use-case make sense?
Shados-Today at 5:12 PM
@tom.watson thats why I export a sendData action creator in my example
so just import {sendData} from './utils/mySocket'; in your container component...(edited)
and use mapDispatchToProps as you would any redux app
kbsymanz-Today at 5:13 PM
@tom.watson My example does not do bi-directional sockets yet. I use thunk for client to server using fetch.
tom.watson-Today at 5:18 PM
@Shados for some reason I wasn't trusting the idea of a direct import, like somehow it won't use the initialized socket connection. @kbsymanz Ahh, ok. I'm going to add a little extra complexity by continuously polling the connection based on a setInterval :) Thanks so much for your thoughts and comments. Gives me many more ideas about how I can tackle this
Shados-Today at 5:18 PM
@tom.watson welcome to the wonderful world of closures, hehe.
still, IMO option #4 is harder than it needs to be.
in my app I used #2, and that worked beautifully
From <https://discordapp.com/channels/102860784329052160/103538784460615680>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment