<commit_before># Re-Frame Simple

[alpha]

A light, beginner-friendly syntax built for `re-frame`.

> Example project: https://github.com/mhuebert/shadow-re-frame
This project includes **re-frame-trace** so you can see how the events dispatched by re-frame-simple can be inspected.

----

Learning `re-frame` involves wrapping one's head around many new words and concepts. However, the basic thing it does is quite simple and shouldn't be hard to get started with.

`re-frame-simple` is a light syntax on top of `re-frame` which feels more like ordinary Clojure. It makes getting started and prototyping easier, without preventing you from moving down the ladder and using lower-level constructs where appropriate.

Three main ideas:

1. treat the `db` as a thing we perform ordinary Clojure operations on
2. reactivity should 'just work'
3. more advanced state management is **opt-in** (eg. named queries and updates, coeffects)

(Syntax is roughly derived from [re-view](https://www.re-view.io) and its associated re-db library.)

### The Basics

Add the dependency (`boot` or `project.clj`):

`[re-view/re-frame-simple "0.1.0]`

Require the namespace:

```clj
(ns my-app.core
  (:require [re-view.re-frame-simple :as db]))
```

**Note** we've aliased `re-view.re-frame-simple` as `db`.

Read from the db using `get`, `get-in` and `identity` functions:

```clj
;; value of the entire db
(db/get :a)        => (get @app-db :a)

;; swap! the entire db
(db/get-in [:a :b]) => (get-in @app-db [:a :b])
```

Well, that was simple. What's so special?

Behind the scenes, `get` and `get-in` map to re-frame **subscriptions**, so when you use these functions to read data, the component you're in will **automatically update** when that data changes.


Write using `assoc!`, `update!`, `assoc-in!`, `update-in!`:


```clj
(db/assoc! :a 1)
(db/assoc-in! [:a :b] 1)

(db/update! :a inc)
(db/update-in! [:a :b] inc)
```

These functions map to re-frame **event handlers** which mutate the current state of the world (the db). They end in `!` as a reminder that you're mutating the world.

Also available are some operations for the whole db (rather than at a particular
key or path). We'll use these less often, as it's easier to understand, inspect, and debug your app when operations are scoped to specific paths.

```clj

(db/identity)     => @app-db

(db/swap! merge {:a 1})
```


### Example

Here is a counter widget which uses `get-in` and `update-in` to read and write a counter, given an id.

```clj
(defn counter
  "Render an interactive counter for `id`"
  [id]
                   ;; NOTICE: `db/update-in!` to write
  [:div {:on-click #(db/update-in! [::counters id] inc)
         :style    {:padding    20
                    :margin     "10px 0"
                    :background "rgba(0,0,0,0.05)"
                    :cursor     "pointer"}}
   (str "Counter " (name id) ": ")

   ;; NOTICE: `db/get-in` to read
   (db/get-in [::counters id])])
```

1. We put counters in a namespaced path in the db (`::counters`). This means I can search across my app for the namespaced `::counters` keyword, and find every instance where a counter is read or mutated. This makes up for some of the explicitness that is lost by moving away from typical, named re-frame events.
2. We didn't have to "register" anything to get a simple example like this to work. There is no inventing of names for transactions as simple as incrementing an integer at a path.

Using these tools, legibility of the reactivity system is built in to the design of your data structure, instead of added via explicitly named events/actions. (but we can still add those, when desired.)


### Named updates

`defupdate` associates a keyword with an update function. This can be dispatched like any other re-frame handler.

```clj
(db/defupdate :initialize [db]
              {::counters {"A" 0
                           "B" 1
                           "C" 2}})
```

Use with `re-frame.core/dispatch` (it's copied into the db namespace):

```clj
(db/dispatch [:initialize])
```

### Named queries

Use `defquery` to create named queries that read data using `db/get` and `db/get-in`:

```clj
(db/defquery counter-list
  "Return the list of counters in the db, by id."
  []
  (-> (db/get ::counters)
      (keys)))
```

The function returns a plain value, but uses a reactive subscription behind the scenes
to trigger reactivity, so a component that uses the query will update when its data changes.

Usage:

```clj
(defn root-view
  "Render the page"
  []
  [:div
   "Click to count!"
                   ;; NOTICE: using our query
   (doall (for [id (counter-list)]
            ^{:key id} [counter id]))])
```

### Wrapping up

I hope you've found this helpful or interesting. Feedback is welcome!<commit_msg>Readme.md<commit_after># Re-Frame Simple

[alpha]

A light, beginner-friendly syntax built for `re-frame`.

----

Learning `re-frame` involves wrapping one's head around many new words and concepts. However, the basic thing it does is quite simple and shouldn't be hard to get started with.

`re-frame-simple` is a light syntax on top of `re-frame` which feels more like ordinary Clojure. It makes getting started and prototyping easier, without preventing you from moving down the ladder and using lower-level constructs where appropriate.

Three main ideas:

1. treat the `db` as a thing we perform ordinary Clojure operations on
2. reactivity should 'just work'
3. more advanced state management is **opt-in** (eg. named queries and updates, coeffects)

(Syntax is roughly derived from [re-view](https://www.re-view.io) and its associated re-db library.)

### The Basics

Add the dependency (`boot` or `project.clj`):

`[re-view/re-frame-simple "0.1.0]`

Require the namespace:

```clj
(ns my-app.core
  (:require [re-view.re-frame-simple :as db]))
```

**Note** we've aliased `re-view.re-frame-simple` as `db`.

Read from the db using `get`, `get-in` and `identity` functions:

```clj
;; value of the entire db
(db/get :a)        => (get @app-db :a)

;; swap! the entire db
(db/get-in [:a :b]) => (get-in @app-db [:a :b])
```

Well, that was simple. What's so special?

Behind the scenes, `get` and `get-in` map to re-frame **subscriptions**, so when you use these functions to read data, the component you're in will **automatically update** when that data changes.


Write using `assoc!`, `update!`, `assoc-in!`, `update-in!`:


```clj
(db/assoc! :a 1)
(db/assoc-in! [:a :b] 1)

(db/update! :a inc)
(db/update-in! [:a :b] inc)
```

These functions map to re-frame **event handlers** which mutate the current state of the world (the db). They end in `!` as a reminder that you're mutating the world.

Also available are some operations for the whole db (rather than at a particular
key or path). We'll use these less often, as it's easier to understand, inspect, and debug your app when operations are scoped to specific paths.

```clj

(db/identity)     => @app-db

(db/swap! merge {:a 1})
```


### Example

Here is a counter widget which uses `get-in` and `update-in` to read and write a counter, given an id.

```clj
(defn counter
  "Render an interactive counter for `id`"
  [id]
                   ;; NOTICE: `db/update-in!` to write
  [:div {:on-click #(db/update-in! [::counters id] inc)
         :style    {:padding    20
                    :margin     "10px 0"
                    :background "rgba(0,0,0,0.05)"
                    :cursor     "pointer"}}
   (str "Counter " (name id) ": ")

   ;; NOTICE: `db/get-in` to read
   (db/get-in [::counters id])])
```

1. We put counters in a namespaced path in the db (`::counters`). This means I can search across my app for the namespaced `::counters` keyword, and find every instance where a counter is read or mutated. This makes up for some of the explicitness that is lost by moving away from typical, named re-frame events.
2. We didn't have to "register" anything to get a simple example like this to work. There is no inventing of names for transactions as simple as incrementing an integer at a path.

Using these tools, legibility of the reactivity system is built in to the design of your data structure, instead of added via explicitly named events/actions. (but we can still add those, when desired.)


### Named updates

`defupdate` associates a keyword with an update function. This can be dispatched like any other re-frame handler.

```clj
(db/defupdate :initialize [db]
              {::counters {"A" 0
                           "B" 1
                           "C" 2}})
```

Use with `re-frame.core/dispatch` (it's copied into the db namespace):

```clj
(db/dispatch [:initialize])
```

### Named queries

Use `defquery` to create named queries that read data using `db/get` and `db/get-in`:

```clj
(db/defquery counter-list
  "Return the list of counters in the db, by id."
  []
  (-> (db/get ::counters)
      (keys)))
```

The function returns a plain value, but uses a reactive subscription behind the scenes
to trigger reactivity, so a component that uses the query will update when its data changes.

Usage:

```clj
(defn root-view
  "Render the page"
  []
  [:div
   "Click to count!"
                   ;; NOTICE: using our query
   (doall (for [id (counter-list)]
            ^{:key id} [counter id]))])
```


----

## Example project

- **Source:** https://github.com/mhuebert/shadow-re-frame
- **Live Demo:** https://mhuebert.github.io/shadow-re-frame/ (be sure to press `Control-H` to see trace panel)

This project includes **re-frame-trace** so you can see how the events dispatched by re-frame-simple can be inspected and provide visibility into the workings of your app.

### Wrapping up

I hope you've found this helpful or interesting. Feedback is welcome!
{'fraction_non_alphanumeric': 0.12133977190999692, 'fraction_numerical': 0.005137162231583273, 'mean_word_length': 3.269298245614035, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 12, 'https://': 5, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7556817', 'n_tokens_mistral': 3107, 'n_tokens_neox': 2926, 'n_words': 1325}