update dependencies and fix things in views.reagent(+ .sente)
This commit is contained in:
parent
b832409dfa
commit
17667c274a
46
README.md
46
README.md
|
@ -1,6 +1,7 @@
|
||||||
# views.reagent
|
# views.reagent
|
||||||
|
|
||||||
[Reagent][1] plugin for the [views][2] library, providing real-time component updates to server-side changes to data.
|
[Reagent][1] plugin for the [views][2] library, providing real-time component updates to
|
||||||
|
server-side changes to data.
|
||||||
|
|
||||||
[1]: https://github.com/reagent-project/reagent
|
[1]: https://github.com/reagent-project/reagent
|
||||||
[2]: https://github.com/gered/views
|
[2]: https://github.com/gered/views
|
||||||
|
@ -11,40 +12,48 @@
|
||||||
This library is made up of two core parts:
|
This library is made up of two core parts:
|
||||||
|
|
||||||
* The actual library, views.reagent, providing core functionality.
|
* The actual library, views.reagent, providing core functionality.
|
||||||
* A client/server communications plugin library which provides the glue code between whatever underlying client/server library you're using (e.g. [Sente][3] or [clj-browserchannel][4]) and views.reagent.
|
* A client/server communications plugin library which provides the glue code between whatever
|
||||||
|
underlying client/server library you're using (most likely [Sente][3]) and views.reagent.
|
||||||
|
|
||||||
[3]: https://github.com/ptaoussanis/sente
|
[3]: https://github.com/ptaoussanis/sente
|
||||||
[4]: https://github.com/gered/clj-browserchannel
|
|
||||||
|
|
||||||
To use views.reagent in your application, you need to add both the main library and one client/server communications plugin library as dependencies. See their respective pages linked to below for more information on doing this.
|
To use views.reagent in your application, you need to add both the main library and one
|
||||||
|
client/server communications plugin library as dependencies. See their respective pages linked
|
||||||
|
to below for more information on doing this.
|
||||||
|
|
||||||
|
|
||||||
### Main Library Documentation
|
### Main Library Documentation
|
||||||
|
|
||||||
[See here for full documentation.][5]
|
[See here for full documentation.][4]
|
||||||
|
|
||||||
[5]: https://github.com/gered/views.reagent/tree/master/views.reagent
|
[4]: https://github.com/gered/views.reagent/tree/master/views.reagent
|
||||||
|
|
||||||
|
|
||||||
### Client/Server Plugin Documentation
|
### Client/Server Plugin Documentation
|
||||||
|
|
||||||
* **[views.reagent.sente][6]** provides fairly low-level integration with Sente.
|
* **[views.reagent.sente][5]** provides fairly low-level integration with Sente.
|
||||||
* **[views.reagent.browserchannel][7]** for using BrowserChannel for client/server communication.
|
|
||||||
|
|
||||||
[6]: https://github.com/gered/views.reagent/tree/master/views.reagent.sente
|
[5]: https://github.com/gered/views.reagent/tree/master/views.reagent.sente
|
||||||
[7]: https://github.com/gered/views.reagent/tree/master/views.reagent.browserchannel
|
|
||||||
|
If you're intent on using something else, you'll need to write your own client/server plugin
|
||||||
|
library. Previously I provided a BrowserChannel plugin in addition to the Sente plugin library,
|
||||||
|
but BrowserChannel is now pretty ancient and unnecessary since modern browsers universally support
|
||||||
|
Websockets, so it was removed in favour of using Sente.
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
There are two example applications for you to look at to see a fully working web application with working views system configured and working.
|
There are two example applications for you to look at to see a fully working web application with
|
||||||
|
working views system configured and working.
|
||||||
|
|
||||||
* Todo MVC. There are two versions of this that are both largely identical except that [one uses Sente][8] and the [other uses BrowserChannel][9].
|
* [Todo MVC][6]. This is a copy of the original Reagent "Todo MVC" example app, but re-worked to
|
||||||
* [Class Registry][10]. This is a somewhat more complex application with a busy UI showing a bunch of data at once, but it does serve to show how a UI can be built from multiple different views at once. This example app uses Sente.
|
use a SQL database and the views system.
|
||||||
|
* [Class Registry][7]. This is a somewhat more complex application with a busy UI showing a bunch
|
||||||
|
of data at once, but it does serve to show how a UI can be built from multiple different views
|
||||||
|
at once.
|
||||||
|
|
||||||
[8]: https://github.com/gered/views.reagent/tree/master/examples/todomvc
|
[6]: https://github.com/gered/views.reagent/tree/master/examples/todomvc
|
||||||
[9]: https://github.com/gered/views.reagent/tree/master/examples/todomvc-browserchannel
|
[7]: https://github.com/gered/views.reagent/tree/master/examples/class-registry
|
||||||
[10]: https://github.com/gered/views.reagent/tree/master/examples/class-registry
|
|
||||||
|
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
@ -55,15 +64,12 @@ like to integrate client/server communications in their applications. I wanted t
|
||||||
(as much as possible) doing anything that would require any specific way of doing this
|
(as much as possible) doing anything that would require any specific way of doing this
|
||||||
kind of integration.
|
kind of integration.
|
||||||
|
|
||||||
As well, speaking for myself, I use my own custom helper library that wraps over Sente which
|
|
||||||
I like but did not want to force anyone else to use.
|
|
||||||
|
|
||||||
The client/server glue code provided by these libraries is incredibly light so if they
|
The client/server glue code provided by these libraries is incredibly light so if they
|
||||||
do not meet your needs for whatever reason you should find it easy to create one yourself.
|
do not meet your needs for whatever reason you should find it easy to create one yourself.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright © 2016 Gered King
|
Copyright © 2022 Gered King
|
||||||
|
|
||||||
Distributed under the the MIT License. See LICENSE for more details.
|
Distributed under the the MIT License. See LICENSE for more details.
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
(defproject gered/views.reagent.sente "0.2-SNAPSHOT"
|
(defproject net.gered/views.reagent.sente "0.2-SNAPSHOT"
|
||||||
:description "Sente client/server messaging adapter for views.reagent."
|
:description "Sente client/server messaging adapter for views.reagent."
|
||||||
:url "https://github.com/gered/views.reagent"
|
:url "https://github.com/gered/views.reagent"
|
||||||
:license {:name "MIT License"
|
:license {:name "MIT License"
|
||||||
:url "http://opensource.org/licenses/MIT"}
|
:url "http://opensource.org/licenses/MIT"}
|
||||||
|
|
||||||
:dependencies [[org.clojure/clojure "1.8.0"]]
|
:dependencies []
|
||||||
|
|
||||||
:plugins [[lein-cljsbuild "1.1.3"]]
|
:plugins [[lein-cljsbuild "1.1.8"]]
|
||||||
|
|
||||||
:profiles {:provided
|
:profiles {:provided
|
||||||
{:dependencies
|
{:dependencies
|
||||||
[[org.clojure/clojure "1.8.0"]
|
[[org.clojure/clojure "1.10.3"]
|
||||||
[org.clojure/clojurescript "1.8.51"]
|
[org.clojure/clojurescript "1.10.773"]
|
||||||
[reagent "0.6.0-alpha"]
|
[reagent "1.1.0"]
|
||||||
[gered/views "1.5"]
|
[net.gered/views "1.6-SNAPSHOT"]
|
||||||
[gered/views.reagent "0.1"]
|
[net.gered/views.reagent "0.2-SNAPSHOT"]
|
||||||
[com.taoensso/sente "1.8.1"]]}}
|
[com.taoensso/sente "1.16.2"]]}}
|
||||||
|
|
||||||
:cljsbuild {:builds
|
:cljsbuild {:builds
|
||||||
{:main
|
{:main
|
||||||
|
|
|
@ -21,6 +21,15 @@
|
||||||
(send-fn sente-chsk-map data))
|
(send-fn sente-chsk-map data))
|
||||||
(reset! send-buffer []))
|
(reset! send-buffer []))
|
||||||
|
|
||||||
|
(defn chsk-open-event?
|
||||||
|
"returns true if the sente event is for a channel-socket state change to 'open' or 'connected'."
|
||||||
|
[{:keys [event] :as ev}]
|
||||||
|
; for :chsk/state events, sente sends the event data in the form [old-state new-state].
|
||||||
|
; we only care about the new state for the purposes of performing this check ...
|
||||||
|
(let [[ev-id ev-data] event]
|
||||||
|
(and (= :chsk/state ev-id)
|
||||||
|
(:open? (second ev-data)))))
|
||||||
|
|
||||||
(defn on-open!
|
(defn on-open!
|
||||||
"should be called when a new Sente connection is established. ev is the event
|
"should be called when a new Sente connection is established. ev is the event
|
||||||
map provided by Sente where id = :chsk/state, and :open? = true. make sure
|
map provided by Sente where id = :chsk/state, and :open? = true. make sure
|
||||||
|
@ -44,14 +53,12 @@
|
||||||
application does not need to do any custom Sente event handling, then you can
|
application does not need to do any custom Sente event handling, then you can
|
||||||
opt to use this event handler."
|
opt to use this event handler."
|
||||||
[sente-chsk-map {:keys [event id client-id] :as ev}]
|
[sente-chsk-map {:keys [event id client-id] :as ev}]
|
||||||
(let [[ev-id ev-data] event]
|
(cond
|
||||||
(cond
|
(chsk-open-event? event)
|
||||||
(and (= :chsk/state ev-id)
|
(on-open! sente-chsk-map ev)
|
||||||
(:open? ev-data))
|
|
||||||
(on-open! sente-chsk-map ev)
|
|
||||||
|
|
||||||
(= :chsk/recv id)
|
(= :chsk/recv id)
|
||||||
(on-receive! sente-chsk-map ev))))
|
(on-receive! sente-chsk-map ev)))
|
||||||
|
|
||||||
(defn init!
|
(defn init!
|
||||||
"performs initial configuration necessary to hook Sente into views.reagent as the
|
"performs initial configuration necessary to hook Sente into views.reagent as the
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
# views.reagent
|
# views.reagent
|
||||||
|
|
||||||
This is the main library for the [views.reagent project][1] which
|
This is the main library for the [views.reagent project][1] which provides the core functionality
|
||||||
provides the core functionality that most of your application code will
|
that most of your application code will make use of.
|
||||||
make use of.
|
|
||||||
|
|
||||||
Familiarity with the [views][2] library is *absolutely crucial* to
|
Familiarity with the [views][2] library is *absolutely crucial* to understanding and usage of
|
||||||
understanding and usage of views.reagent.
|
views.reagent.
|
||||||
|
|
||||||
[1]: https://github.com/gered/views.reagent
|
[1]: https://github.com/gered/views.reagent
|
||||||
[2]: https://github.com/gered/views
|
[2]: https://github.com/gered/views
|
||||||
|
@ -18,22 +17,19 @@ understanding and usage of views.reagent.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Much of this documentation will be referring to the
|
Much of this documentation will be referring to the [Todo MVC example project][3] which uses
|
||||||
[Todo MVC example project][3] which uses
|
|
||||||
[Sente client/server messaging][4] and [SQL views][5].
|
[Sente client/server messaging][4] and [SQL views][5].
|
||||||
|
|
||||||
[3]: https://github.com/gered/views.reagent/tree/master/examples/todomvc
|
[3]: https://github.com/gered/views.reagent/tree/master/examples/todomvc
|
||||||
[4]: https://github.com/gered/views.reagent/tree/master/views.reagent.sente
|
[4]: https://github.com/gered/views.reagent/tree/master/views.reagent.sente
|
||||||
[5]: https://github.com/gered/views.sql
|
[5]: https://github.com/gered/views.sql
|
||||||
|
|
||||||
Usage of this library is incredibly simple once you have a working
|
Usage of this library is incredibly simple once you have a working views system up and running.
|
||||||
views system up and running.
|
|
||||||
|
|
||||||
Initialization of the views system is typically either done directly
|
Initialization of the views system is typically either done directly via `views.core/init!` with
|
||||||
via `views.core/init!` with some special configuration for whatever
|
some special configuration for whatever client/server messaging plugin you're using, or you may be
|
||||||
client/server messaging plugin you're using, or you may be required to
|
required to call a special "init" function provided by the plugin library to use instead of
|
||||||
call a special "init" function provided by the plugin library to use
|
`views.core/init!`.
|
||||||
instead of `views.core/init!`.
|
|
||||||
|
|
||||||
The Todo MVC example uses the following (server-side) view system:
|
The Todo MVC example uses the following (server-side) view system:
|
||||||
|
|
||||||
|
@ -49,31 +45,26 @@ The Todo MVC example uses the following (server-side) view system:
|
||||||
[(view :todos db #'todos-list)])
|
[(view :todos db #'todos-list)])
|
||||||
```
|
```
|
||||||
|
|
||||||
A single view named `:todos` which simply returns a list of all Todos
|
A single view named `:todos` which simply returns a list of all Todos in the database. The view
|
||||||
in the database. The view takes no parameters.
|
takes no parameters.
|
||||||
|
|
||||||
Over on the ClojureScript side of things, once a connection has been
|
Over on the ClojureScript side of things, once a connection has been established to the server by
|
||||||
established to the server by the client/server messaging library, you
|
the client/server messaging library, you are ready to start using **view cursors** in your Reagent
|
||||||
are ready to start using **view cursors** in your Reagent components.
|
components.
|
||||||
|
|
||||||
### View Cursors
|
### View Cursors
|
||||||
|
|
||||||
A **view cursor** is simply a Reagent cursor that represents the
|
A **view cursor** is simply a Reagent cursor that represents the underlying view data received
|
||||||
underlying view data received from the views library when the view is
|
from the views library when the view is subscribed to. We can create a subscription by simply
|
||||||
subscribed to. We can create a subscription by simply creating a view
|
creating a view cursor for the desired view and giving it any appropriate parameters.
|
||||||
cursor for the desired view and giving it any appropriate parameters.
|
views.reagent will automatically determine if it's the first usage of the view cursor and if a
|
||||||
views.reagent will automatically determine if it's the first usage of
|
subscription request needs to be sent to the server. When a view refresh is performed on the
|
||||||
the view cursor and if a subscription request needs to be sent to the
|
server for the view, views.reagent sends it to the client and the data is put into a location
|
||||||
server. When a view refresh is performed on the server for the view,
|
where it's available to the views cursor. Since view cursors are Reagent cursors, updating the
|
||||||
views.reagent sends it to the client and the data is put into a
|
data like this instantly causes components dereferencing the cursor to rerender themselves. When
|
||||||
location where it's available to the views cursor. Since view cursors
|
components are unmounted views.reagent will automatically unsubscribe from views as appropriate.
|
||||||
are Reagent cursors, updating the data like this instantly causes
|
As well, when parameters passed in to view cursors change, view re-subscriptions are automatically
|
||||||
components dereferencing the cursor to rerender themselves. When
|
handled to make sure the view cursor is always up to date with the current client state.
|
||||||
components are unmounted views.reagent will automatically unsubscribe
|
|
||||||
from views as appropriate. As well, when parameters passed in to view
|
|
||||||
cursors change, view re-subscriptions are automatically handled to make
|
|
||||||
sure the view cursor is always up to date with the current client
|
|
||||||
state.
|
|
||||||
|
|
||||||
So, how do we create a view cursor?
|
So, how do we create a view cursor?
|
||||||
|
|
||||||
|
@ -91,36 +82,29 @@ So, how do we create a view cursor?
|
||||||
@(view-cursor :todos))])
|
@(view-cursor :todos))])
|
||||||
```
|
```
|
||||||
|
|
||||||
Given the previously set up view system on the server, this is all the
|
Given the previously set up view system on the server, this is all the UI code necessary to
|
||||||
UI code necessary to subscribe to the `:todos` view and retrieve and
|
subscribe to the `:todos` view and retrieve and render the Todos list on the client. Whenever the
|
||||||
render the Todos list on the client. Whenever the `:todos` view is
|
`:todos` view is refreshed on the server, the client will receive the data automatically and the
|
||||||
refreshed on the server, the client will receive the data automatically
|
component will rerender since it is dereferencing the views cursor. Finally, the client will
|
||||||
and the component will rerender since it is dereferencing the views
|
automatically unsubscribe from the `:todos` view when the `my-todos-list` component is unmounted.
|
||||||
cursor. Finally, the client will automatically unsubscribe from the
|
|
||||||
`:todos` view when the `my-todos-list` component is unmounted.
|
|
||||||
|
|
||||||
At first glance, `my-todos-list` looks just like any other normal
|
At first glance, `my-todos-list` looks just like any other normal Reagent component. However a
|
||||||
Reagent component. However a very important difference is the use of
|
very important difference is the use of `defvc` instead of `defn`.
|
||||||
`defvc` instead of `defn`.
|
|
||||||
|
|
||||||
`defvc` creates a Reagent **view component** which hooks into some
|
`defvc` creates a Reagent **view component** which hooks into some React component lifecycle
|
||||||
React component lifecycle events to automatically handle view
|
events to automatically handle view subscriptions/unsubscriptions for us based on how we use
|
||||||
subscriptions/unsubscriptions for us based on how we use `view-cursor`
|
`view-cursor` inside the component. You ***must*** use `defvc` for all Reagent components within
|
||||||
inside the component. You ***must*** use `defvc` for all Reagent
|
which you want to use `view-cursor`.
|
||||||
components within which you want to use `view-cursor`.
|
|
||||||
|
|
||||||
`view-cursor` returns a Reagent cursor containing the actual view data
|
`view-cursor` returns a Reagent cursor containing the actual view data (in this case, a simple
|
||||||
(in this case, a simple list of Todos). It's important to note that at
|
list of Todos). It's important to note that at first the view data returned will be `nil` since
|
||||||
first the view data returned will be `nil` since obviously the client
|
obviously the client must first wait for the subscription to be processed by the server and then
|
||||||
must first wait for the subscription to be processed by the server and
|
for the server to send back the initial view data. Once this happens the component will
|
||||||
then for the server to send back the initial view data. Once this
|
automatically rerender as you would expect (showing the list of todos).
|
||||||
happens the component will automatically rerender as you would expect
|
|
||||||
(showing the list of todos).
|
|
||||||
|
|
||||||
You can check if the view cursor is still waiting on the initial set of
|
You can check if the view cursor is still waiting on the initial set of data through the use of
|
||||||
data through the use of the `loading?` function. This can be used to
|
the `loading?` function. This can be used to render some kind of "loading" message or something
|
||||||
render some kind of "loading" message or something similar if you'd
|
similar if you'd prefer not to render empty data when components first load.
|
||||||
prefer not to render empty data when components first load.
|
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(defvc my-todos-list []
|
(defvc my-todos-list []
|
||||||
|
@ -135,20 +119,18 @@ prefer not to render empty data when components first load.
|
||||||
@todos)])))
|
@todos)])))
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that `loading?` should be passed the actual Reagent cursor that
|
Note that `loading?` should be passed the actual Reagent cursor that `view-cursor` returns, not
|
||||||
`view-cursor` returns, not the dereferenced result.
|
the dereferenced result.
|
||||||
|
|
||||||
Also remember that `loading?` only checks if the view cursor is waiting
|
Also remember that `loading?` only checks if the view cursor is waiting on the **initial** view
|
||||||
on the **initial** view data. Once that first set of data is received,
|
data. Once that first set of data is received, `loading?` will always return false. There is no
|
||||||
`loading?` will always return false. There is no current method in
|
current method in views.reagent for determining if a view refresh is pending, although this is
|
||||||
views.reagent for determining if a view refresh is pending, although
|
typically somewhat of a less drastic UI change to the user so in practice it may be less of a
|
||||||
this is typically somewhat of a less drastic UI change to the user so
|
concern.
|
||||||
in practice it may be less of a concern.
|
|
||||||
|
|
||||||
#### View Parameters
|
#### View Parameters
|
||||||
|
|
||||||
Some of your views may take parameters. This is easily supported by
|
Some of your views may take parameters. This is easily supported by views.reagent.
|
||||||
views.reagent.
|
|
||||||
|
|
||||||
As an example, if our `:todos` view was updated to include a filter:
|
As an example, if our `:todos` view was updated to include a filter:
|
||||||
|
|
||||||
|
@ -182,26 +164,23 @@ Letting us do any of these on the client:
|
||||||
|
|
||||||
#### View Cursors Are Intended To Be Read-only
|
#### View Cursors Are Intended To Be Read-only
|
||||||
|
|
||||||
Even though a Reagent cursor allows you to update them as well as read
|
Even though a Reagent cursor allows you to update them as well as read from them, updating a view
|
||||||
from them, updating a view cursor doesn't do anything. Nothing stops
|
cursor doesn't do anything. Nothing stops you from doing this, but updating a view cursor does not
|
||||||
you from doing this, but updating a view cursor does not propagate
|
propagate changes to the server or anything like that. In addition, you will lose any changes you
|
||||||
changes to the server or anything like that. In addition, you will lose
|
make every time a view refresh is received as the data gets blindly replaced.
|
||||||
any changes you make every time a view refresh is received as the data
|
|
||||||
gets blindly replaced.
|
|
||||||
|
|
||||||
It is recommended that you do not write code that updates a view cursor.
|
It is recommended that you do not write code that updates a view cursor.
|
||||||
|
|
||||||
## Advanced Topics
|
## Advanced Topics
|
||||||
|
|
||||||
Most people just looking to use views.reagent in their applications
|
Most people just looking to use views.reagent in their applications probably won't need to read
|
||||||
probably won't need to read anything in this section.
|
anything in this section.
|
||||||
|
|
||||||
### Manually Managing View Subscriptions
|
### Manually Managing View Subscriptions
|
||||||
|
|
||||||
For those applications with very specific/complex requirements, you can
|
For those applications with very specific/complex requirements, you can manually subscribe and
|
||||||
manually subscribe and unsubscribe to views from your ClojureScript
|
unsubscribe to views from your ClojureScript code using views.reagent as well as make use of view
|
||||||
code using views.reagent as well as make use of view cursors outside of
|
cursors outside of Reagent components. I do not recommend this though.
|
||||||
Reagent components. I do not recommend this though.
|
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(use 'views.reagent.client.core)
|
(use 'views.reagent.client.core)
|
||||||
|
@ -217,50 +196,41 @@ Reagent components. I do not recommend this though.
|
||||||
(unsubscribe! [{:view-id :todos :parameters []}])
|
(unsubscribe! [{:view-id :todos :parameters []}])
|
||||||
```
|
```
|
||||||
|
|
||||||
When using these low-level functions, you need to specify view
|
When using these low-level functions, you need to specify view signature maps (a.k.a. "view sigs")
|
||||||
signature maps (a.k.a. "view sigs") to refer to the views you want to
|
to refer to the views you want to use. Also note that unlike the server-side view signatures that
|
||||||
use. Also note that unlike the server-side view signatures that you may
|
you may be familiar with from the views library, these client-side view signatures never have a
|
||||||
be familiar with from the views library, these client-side view
|
`:namespace` in them. Even if you include one, it is disregarded by the server.
|
||||||
signatures never have a `:namespace` in them. Even if you include one,
|
|
||||||
it is disregarded by the server.
|
|
||||||
|
|
||||||
Also note that `subscribe!` and `unsubscribe!` both take a list of view
|
Also note that `subscribe!` and `unsubscribe!` both take a list of view signatures, so you can
|
||||||
signatures, so you can subscribe and unsubscribe from multiple views at
|
subscribe and unsubscribe from multiple views at once.
|
||||||
once.
|
|
||||||
|
|
||||||
I do not recommend mixing use of these low-level functions and using
|
I do not recommend mixing use of these low-level functions and using `defvc` components. You
|
||||||
`defvc` components. You should probably pick one or the other and stick
|
should probably pick one or the other and stick to it unless you really know what you're doing.
|
||||||
to it unless you really know what you're doing.
|
|
||||||
|
|
||||||
### Integration Points for Writing a Client/Server Messaging Plugin
|
### Integration Points for Writing a Client/Server Messaging Plugin
|
||||||
|
|
||||||
If you would like to write your own client/server messaging plugin
|
If you would like to write your own client/server messaging plugin library to fit your own needs
|
||||||
library to fit your own needs you can easily do so. There are a couple
|
you can easily do so. There are a couple integration points within the main views.reagent library,
|
||||||
integration points within the main views.reagent library, as well as
|
as well as some special configuration you will need to provide to the views system that you need
|
||||||
some special configuration you will need to provide to the views system
|
to be aware of.
|
||||||
that you need to be aware of.
|
|
||||||
|
|
||||||
#### View Subscriber Key
|
#### View Subscriber Key
|
||||||
|
|
||||||
In the views library, a bunch of functions take a "subscriber key."
|
In the views library, a bunch of functions take a "subscriber key." This is an arbitrary value
|
||||||
This is an arbitrary value that uniquely identifies a subscriber. There
|
that uniquely identifies a subscriber. There can of course be multiple views for each subscriber
|
||||||
can of course be multiple views for each subscriber key. Said another
|
key. Said another way: someone (uniquely identified by the subscriber key) can be subscribed to
|
||||||
way: someone (uniquely identified by the subscriber key) can be
|
multiple different views at the same time.
|
||||||
subscribed to multiple different views at the same time.
|
|
||||||
|
|
||||||
You will typically want to use the underlying client/server library's
|
You will typically want to use the underlying client/server library's "client/user connection ID"
|
||||||
"client/user connection ID" as the subscriber key. For Sente this is
|
as the subscriber key. For Sente this is almost certainly going to be the "user id" or the
|
||||||
the "user id" or the "client id" (depending on your application), and
|
"client id" (depending on your application).
|
||||||
for clj-browserchannel this is the BrowserChannel "session id."
|
|
||||||
|
|
||||||
#### View System Configuration
|
#### View System Configuration
|
||||||
|
|
||||||
You need to provide a `:send-fn` function that can be provided in the
|
You need to provide a `:send-fn` function that can be provided in the options given to
|
||||||
options given to `views.core/init!`. This function is used by the views
|
`views.core/init!`. This function is used by the views library to send view refreshes to
|
||||||
library to send view refreshes to subscribers. For views.reagent, your
|
subscribers. For views.reagent, your send-fn function should send a vector with 3 things in it:
|
||||||
send-fn function should send a vector with 3 things in it: the keyword
|
the keyword `:views/refresh` followed by the `view-sig` and then `view-data`. For example:
|
||||||
`:views/refresh` followed by the `view-sig` and then `view-data`. For
|
|
||||||
example:
|
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
(defn send-fn
|
(defn send-fn
|
||||||
|
@ -270,50 +240,43 @@ example:
|
||||||
|
|
||||||
#### Server-side Handling
|
#### Server-side Handling
|
||||||
|
|
||||||
`views.reagent.server.core` has two main event handling functions that
|
`views.reagent.server.core` has two main event handling functions that provide proper handling
|
||||||
provide proper handling for client connection events:
|
for client connection events:
|
||||||
|
|
||||||
* `on-close!` should be called when a client's connection is closed for
|
* `on-close!` should be called when a client's connection is closed for whatever reason.
|
||||||
whatever reason. views.reagent will remove all of the client's
|
views.reagent will remove all of the client's subscriptions.
|
||||||
subscriptions.
|
* `on-receive!` should be called and passed in the raw data from every message received from the
|
||||||
* `on-receive!` should be called and passed in the raw data from every
|
client. It will return `true` if views.reagent recognized and handled the message as a
|
||||||
message received from the client. It will return `true` if
|
subscription or unsubscription request. See `views.reagent.utils/relevant-event?` for how it
|
||||||
views.reagent recognized and handled the message as a subscription or
|
recognizes relevant messages.
|
||||||
unsubscription request. See `views.reagent.utils/relevant-event?` for
|
|
||||||
how it recognizes relevant messages.
|
|
||||||
|
|
||||||
For both of these functions, `client-id` should be the subscriber key
|
For both of these functions, `client-id` should be the subscriber key from the views library, and
|
||||||
from the views library, and `context` can be anything you wish.
|
`context` can be anything you wish. Typically you will want to use something like a Ring request
|
||||||
Typically you will want to use something like a Ring request map and/or
|
map and/or user profile data as the context. This context argument is what gets passed to
|
||||||
user profile data as the context. This context argument is what gets
|
`views.core/subscribe!` and `views.core/unsubscribe!`.
|
||||||
passed to `views.core/subscribe!` and `views.core/unsubscribe!`.
|
|
||||||
|
|
||||||
#### Client-side Handling
|
#### Client-side Handling
|
||||||
|
|
||||||
`views.reagent.client.core` also has two main event handling functions
|
`views.reagent.client.core` also has two main event handling functions that provide proper
|
||||||
that provide proper handling for server connection events:
|
handling for server connection events:
|
||||||
|
|
||||||
* `on-open!` should be called when a connection to the server is
|
* `on-open!` should be called when a connection to the server is established (and re-established,
|
||||||
established (and re-established, if applicable). views.reagent will
|
if applicable). views.reagent will re-send subscription requests for any subscriptions that
|
||||||
re-send subscription requests for any subscriptions that should exist
|
should exist (e.g. if the connection was lost and the application reconnected, resubscribe to
|
||||||
(e.g. if the connection was lost and the application reconnected,
|
the views that all current mounted components need).
|
||||||
resubscribe to the views that all current mounted components need).
|
* `on-receive!` should be called and passed in the raw data from every message received from the
|
||||||
* `on-receive!` should be called and passed in the raw data from every
|
server. It will return `true` if views.reagent recognized and handled the message as a view
|
||||||
message received from the server. It will return `true` if
|
refresh event. See `views.reagent.utils/relevant-event?` for how it recognizes relevant messages.
|
||||||
views.reagent recognized and handled the message as a view refresh
|
|
||||||
event. See `views.reagent.utils/relevant-event?` for how it recognizes
|
|
||||||
relevant messages.
|
|
||||||
|
|
||||||
You also need to provide a function to send messages to the server.
|
You also need to provide a function to send messages to the server. You can set this function by
|
||||||
You can set this function by directly using `reset!` on the atom
|
directly using `reset!` on the atom `views.reagent.client/send-fn`. The function should take a
|
||||||
`views.reagent.client/send-fn`. The function should take a single
|
single argument which is the data to be sent.
|
||||||
argument which is the data to be sent.
|
|
||||||
|
|
||||||
You should take care to hook up all of these integration points before
|
You should take care to hook up all of these integration points before the first Reagent component
|
||||||
the first Reagent component is rendered at page load.
|
is rendered at page load.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright © 2016 Gered King
|
Copyright © 2022 Gered King
|
||||||
|
|
||||||
Distributed under the the MIT License. See LICENSE for more details.
|
Distributed under the the MIT License. See LICENSE for more details.
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
(defproject gered/views.reagent "0.2-SNAPSHOT"
|
(defproject net.gered/views.reagent "0.2-SNAPSHOT"
|
||||||
:description "Reagent plugin for the views library, providing real-time component updates to server-side changes to data."
|
:description "Reagent plugin for the views library, providing real-time component updates to server-side changes to data."
|
||||||
:url "https://github.com/gered/views.reagent"
|
:url "https://github.com/gered/views.reagent"
|
||||||
:license {:name "MIT License"
|
:license {:name "MIT License"
|
||||||
:url "http://opensource.org/licenses/MIT"}
|
:url "http://opensource.org/licenses/MIT"}
|
||||||
|
|
||||||
:dependencies [[org.clojure/tools.logging "0.3.1"]]
|
:dependencies [[org.clojure/tools.logging "1.2.4"]]
|
||||||
|
|
||||||
:plugins [[lein-cljsbuild "1.1.3"]]
|
:plugins [[lein-cljsbuild "1.1.8"]]
|
||||||
|
|
||||||
:profiles {:provided
|
:profiles {:provided
|
||||||
{:dependencies
|
{:dependencies
|
||||||
[[org.clojure/clojure "1.8.0"]
|
[[org.clojure/clojure "1.10.3"]
|
||||||
[org.clojure/clojurescript "1.8.51"]
|
[org.clojure/clojurescript "1.10.773"]
|
||||||
[reagent "0.6.0-alpha"]
|
[reagent "1.1.0"]
|
||||||
[gered/views "1.5"]]}}
|
[net.gered/views "1.6-SNAPSHOT"]]}}
|
||||||
|
|
||||||
:cljsbuild {:builds
|
:cljsbuild {:builds
|
||||||
{:main
|
{:main
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
[args & body] decl]
|
[args & body] decl]
|
||||||
`(defn ~component-name ~attr-map []
|
`(defn ~component-name ~attr-map []
|
||||||
(reagent.core/create-class
|
(reagent.core/create-class
|
||||||
{:component-will-mount
|
{:constructor
|
||||||
(fn [this#]
|
(fn [this# props#]
|
||||||
(views.reagent.client.component/prepare-for-render! this#))
|
(views.reagent.client.component/prepare-for-render! this#))
|
||||||
|
|
||||||
:component-did-mount
|
:component-did-mount
|
||||||
|
@ -43,14 +43,11 @@
|
||||||
(fn [this#]
|
(fn [this#]
|
||||||
(views.reagent.client.component/unsubscribe-all! this#))
|
(views.reagent.client.component/unsubscribe-all! this#))
|
||||||
|
|
||||||
:component-will-receive-props
|
|
||||||
(fn [this# new-argv#]
|
|
||||||
(views.reagent.client.component/prepare-for-render! this#))
|
|
||||||
|
|
||||||
:component-did-update
|
:component-did-update
|
||||||
(fn [this# old-argv#]
|
(fn [this# old-argv#]
|
||||||
(views.reagent.client.component/update-subscriptions! this#))
|
(views.reagent.client.component/update-subscriptions! this#))
|
||||||
|
|
||||||
:component-function
|
:reagent-render
|
||||||
(fn ~args
|
(fn ~args
|
||||||
|
(views.reagent.client.component/prepare-for-render! (reagent.core/current-component))
|
||||||
~@body)}))))
|
~@body)}))))
|
||||||
|
|
Loading…
Reference in a new issue