Created
April 20, 2016 00:00
-
-
Save thomasgwatson/1cda6b7d611ad9e1a36c3d01bf5a3e36 to your computer and use it in GitHub Desktop.
Discussion about persistent connections and React Redux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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