React is a rendering library. That is, you create a tree of widgets (components in react lingo) and feed them data. React then manages the rendering (turning it into the DOM) for you whenever this data is changed. It does it in a generally efficient way.
Re-frame is a flux-like, elm-like uni-directional data flow framework that uses React as its rendering component. What re-frame gives you is a message bus to which you can dispatch messages from DOM events (eg user clicks button and you dispatch a user-defined message), a handler registration system which allows you to register functions to process messages[1], and a subscription system which allows you to "subscribe" to some view over your applications state in a way that if this view changes, your react components get fed new data and get rerendered (if necessary). Re-frame also has mechanisms to facilitate effectual handlers (that is, handlers which effect the outside world rather than being a pure function of (app state, message)->new app state; like normal handlers are).
That is, react does rendering, re-frame uses react and is a complete application framework that makes it easier to build large complex applications by allowing you to split your application into smaller (mostly pure-functional) pieces that communicate through the messaging system.
[1] Message handlers take in the message and the current application state and return a new version of the application state. That is, they are pure functions that move the application to its next state. Handlers are quite powerful and support "interceptors" (decorators/middleware for messages basically) and there are effect and coeffect handlers for managing side-effects (like dispatching new messages in a feedback loop, or talking to a server, for example).
Thank you for your great explanation. I've read the documentation of re-frame, but I still can't figure out how it deals with reusable components and hierarchies.
Let's say I want to write a reusable datepicker component which sends an event like [:date [2017 1 16]] when the user clicks on that date. How can it send it to the calling component? The re-frame pub/sub message bus seems to be global, but what if I want to have several instances of the same component on the page?
The only solution I can come up with is to parameterize the component on instantiation, so that if I have a "person" component that uses the datepicker to set the day of birth of the person, the date-picker would be parameterized so that it would emit a global event such as [:set-person-day-of-birth <person-id> [2017 1 16]]. Is this how re-frame approaches hierarchies?
I would say you don't. I think about it this way: use re-frame to write applications, but components must be self contained and have no knowledge of reframe. To do what you asked, I'll pass a function to the component. The function can then make the context-aware reframe call. That way all your component needs to do is to call the passed in function.
It depends on what you mean by component. Reagent components (ie UI widgets), I agree. Components should be self-contained and as re-frame agnostic as possible. So instead of subscribing to the data they want, they could take in a ratom as an argument (because the subscribe function returns one anyway).
But if the component is larger (that is, not just the view, but also the handlers and subscriptions -- I'd probably call it something else, a service maybe...) then its a bit less simple because not just the view, but also the handlers and subscriptions need to be aware of the "instance" they are running. There's no well defined re-frame solution for this yet.
That is, by convention, subscriptions are always [query-name instance-id <other optional data>] and messages are always [message-type instance-id <other optional data>] by convention. This way works quite well, but has the downside that it is by convention and there's no guarantee that other code will do it the same way.
Of course yes. My reply was largely tied to the parent's description of a date-picker component. But for application level components I'd use event names that are unique at the application level that conveys the intended semantics of the event. Everything else becomes an argument to that name, which pretty much aligns with your notion of instance Id et al although I'd be less rigid about the particular structure of the arguments.
although I'd be less rigid about the particular structure of the arguments.
My reasoning was for conpatibility and consistency. If everyone makes up their own argument structure, it becomes very difficult to maintain and puts the onus on the developers to remember which components use which structure. I'm not really asking for much beyond standardising that the first argument of an instanced event of subscription query is the instance id.
Re-frame is a flux-like, elm-like uni-directional data flow framework that uses React as its rendering component. What re-frame gives you is a message bus to which you can dispatch messages from DOM events (eg user clicks button and you dispatch a user-defined message), a handler registration system which allows you to register functions to process messages[1], and a subscription system which allows you to "subscribe" to some view over your applications state in a way that if this view changes, your react components get fed new data and get rerendered (if necessary). Re-frame also has mechanisms to facilitate effectual handlers (that is, handlers which effect the outside world rather than being a pure function of (app state, message)->new app state; like normal handlers are).
That is, react does rendering, re-frame uses react and is a complete application framework that makes it easier to build large complex applications by allowing you to split your application into smaller (mostly pure-functional) pieces that communicate through the messaging system.
[1] Message handlers take in the message and the current application state and return a new version of the application state. That is, they are pure functions that move the application to its next state. Handlers are quite powerful and support "interceptors" (decorators/middleware for messages basically) and there are effect and coeffect handlers for managing side-effects (like dispatching new messages in a feedback loop, or talking to a server, for example).