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