Handling Uncontrolled Third Party Objects
Not everything in the world is functional and immutable, and occasionally your application needs to manage these things. Ferp provides effects and subscriptions for handling these types of systems, but it's not always obvious which one to use and how to implement it.
Rebuild objects by storing builder values
In the example of firebase, you may have a document reference that is constructed by:
Copy firebase .database (app) .ref () .collection ( 'foo' ) .document ( 'bar' )
Copy export const firebaseReadFx = (path , afterReadAction) => effects .defer ((resolve) => {
firebase .database (app) .ref (path) .snapshot ((value) => {
resolve ( effects .act (afterReadAction , value));
});
});
// usage: firebaseReadFx('foo/bar', actionThatAcceptsValue)
If you have objects like websockets, those are perfect for subscriptions. Here's how I would set it up:
Copy import { app , effects } from 'ferp' ;
const INITIAL_STATE = {
websocket_url : 'wss://echo.websocket.org' ,
websocket : null ,
messages : [] ,
};
const WebsocketSendFx = (data , websocket) => effects .thunk (() => {
if (websocket) websocket .send (data);
return effects .none ();
});
const WebsocketSend = data => state => [
state ,
WebsocketSendFx (data , state .websocket) ,
];
const OnConnected = (websocket) => (state) => [
{ ... state , websocket } ,
effects .act (WebsocketSend , 'hello, world' ) ,
];
const OnDisconnected = (state) => [
{ ... state , websocket : null } ,
effects .none () ,
];
const OnMessage = (message) => (state) => [
{ ... state , messages : state . messages .concat (message) } ,
effects .none () ,
};
const WebsocketSubscription = (dispatch , websocketUrl , ConnectAction , DisconnectAction , MessageAction) => {
const websocket = new WebSocket (websocketUrl);
const onOpen = () => dispatch ( ConnectAction (websocket));
const onClose = () => dispatch (Disconnect);
const onMessage = ({ data }) => dispatch ( MessageAction (data));
websocket .addEventListener ( 'open' , onOpen);
websocket .addEventListener ( 'close' , onClose);
websocket .addEventListener ( 'message' , onMessage);
return () => {
websocket .removeEventListener ( 'open' , onOpen);
websocket .removeEventListener ( 'close' , onClose);
websocket .removeEventListener ( 'message' , onMessage);
websocket .close ();
};
};
const dispatch = app ({
init : [ INITIAL_STATE , effects .none ()] ,
subscribe : state => [
state .websocketUrl && [
WebsocketSubscription ,
state .websocketUrl ,
OnConnect ,
OnDisconnect ,
OnMessage ,
] ,
] ,
});