Fork me on GitHub
#re-frame
<
2019-09-06
>
Leon11:09:55

How Do you approach testing and even just repl driven development with re-frame? Currently im subscribing to All my State in top-Level components and then passing that down to child components as props, wich makes it easy to test These child components. But as i understand, thats not the recommended approach in re-frame. But if every little components is aware of the App-DB, and is subscribing to it directly without any parameterization of that DB access, how Do i test that component (without having to create the whole DB, as i said, repl driven stuff is a goal too)

manutter5111:09:00

You can write each subscription/event-handler as a function first, and then just use reg-sub / reg-event-db to register the function. Then your testing / REPL workflow is the same as for any other function.

Leon11:09:37

But then the view-functions are still impure, and as such harder to test. Currently i like fighting terrible error messages by just evaluating view functions in the repl and Look at the output to See whats wrong. Is this workflow still somehow possible with subscriptions and such?

Leon11:09:13

Is there some idiomatic way to keep view components pure while still benefitting from the db

manutter5112:09:56

I actually did come up with an approach that lets you easily mock out subscriptions so you can do testing on your components. What I did was instead of hard-coding my subscriptions inside the component, I pass in the subscription vectors in an opts map as an argument to the component function. Then I use a custom subscribe-to function do do the actual subscribing inside the component. If the subscription vector is an actual vector, then it just calls re-frame/subscribe, as usual, BUT if it’s an atom, it just returns the atom, which behaves the same as a subscription inside the component. Thus, you can test by passing in an opts map with atoms instead of subscription vectors and inspecting the result.

manutter5112:09:13

(defn atom?
  [maybe-atom]
  (or (instance? reagent.ratom/RAtom maybe-atom)
      (instance? cljs.core/Atom maybe-atom)
      (instance? reagent.ratom/Reaction maybe-atom)))

(defn subscribe-to
  "If arg is a re-frame subscription vector, returns subscription.
  If arg is an atom or a Reagent ratom, returns the atom/ratom.
  Otherwise throws an exception."
  [vec-or-atom]
  (cond
    (vector? vec-or-atom) (rf/subscribe vec-or-atom)
    (atom? vec-or-atom) vec-or-atom
    :else (throw (ex-info "Invalid argument to subscribe-to" vec-or-atom))))

(defn my-component
  [opts]
  (let [{:keys [some-sub]} opts
        sub-some (subscribe-to some-sub)]
    [:div (str "Your subscription is " @sub-some)]))

Leon12:09:58

thats a cool approach! but in this case you'll need to specify the subscription at the parent element anyways, right? so "self-aware" components aren't really possible. and in that case, what would be the benefit over just passing in the direct values as props from the parent, and then doing the subscriptions at one top-level page component like i currently do?

Leon12:09:08

i guess this is, just by the very nature of a global state db, an unsolvable problem... its like i have to choose between the systemic elegance of a global db and the conceptual simplicity of localized state

manutter5112:09:43

The advantage of subscriptions is that they use React to only re-render the components whose underlying data is actually changing. If you subscribe once at the top level, you’ll re-render every component whenever any other component changes.

Leon12:09:55

as im not using a lot of state (pretty much just fetching some data from an api and displaying it, for now i keep things like the current value of a form field in local state) that shouldn't hurt really, and if it does, ill choose your propsed approach ^^ thanks man ;D

knubie12:09:45

There's another approach that is popular in the React world of creating impure "container" components that render your "pure" components

knubie12:09:10

Using pure components is nice because you can use spec to generate dummy date for them and rapidly QA with something devcards

eag16:09:13

Do you have or know of an example that does that, Im interested in using spec with re-frame. Dev cards too. But one thing at a time...

Leon13:09:40

thats pretty much what im doing, just that im combining multiple containers into one, i guess if i run into performance problems ill do that ^^