Fork me on GitHub
#reagent
<
2017-07-29
>
oskarkv18:07:17

So the components rerender when a reagent/atom changes, if they deref the atom. So must I split up my state into several atoms to avoid redrawing everything all the time? I prefer to have one atom for the whole app.

noisesmith18:07:52

@oskarkv that's what cursors are for

oskarkv18:07:11

Alright, let's see what cursors are. 🙂

juhoteperi20:07:53

Huh, I see :exclusions and empty react namespaces mentioned here, luckily we are getting very close to fixing this properly: https://github.com/reagent-project/reagent/pull/306

juhoteperi20:07:36

I'm thinking 0.8 will include only this change, as this will require ClojureScript 1.9.854 and it will take some time for everyone to upgrade to that

juhoteperi20:07:35

Would be cool if someone can test the change with :target :nodejs or Electron. I have tested with npm-deps and Cljsjs package.

juhoteperi20:07:47

Or React-native. I don't have much idea how that works.

gadfly36120:07:03

@juhoteperi 0.8.0-snapshot sounds like a great idea!

pesterhazy20:07:16

does this mean you won't be able to use Reagent without a node_modules folder anymore?

juhoteperi21:07:38

As the PR description mentions, (:require [react-dom :as react-dom]) works with new Cljsjs packages, node_modules and NodeJS target

juhoteperi21:07:49

It generates different JS code depending on the environment

juhoteperi21:07:04

This will need some documentation and lots of Cljsjs updates, because every Cljsjs React addon package needs to be updated to use new name, react instead of cljsjs.react

juhoteperi21:07:13

The new name is required so that it matches with npm name

pesterhazy21:07:33

in cljsrn, people typically get their react using (js/require "react")

pesterhazy21:07:43

which works in figwheel as well as in production bundles

juhoteperi21:07:46

Hmm, I will have to investigate if it would be possible to provide both cljsjs.react and react from same foreign-lib so that existing react-* packages would work

juhoteperi21:07:19

Do React-native projects use :target :nodejs, if so, (:require [react :as react]) will generate require("react") call

pesterhazy21:07:28

no they don't normally

juhoteperi21:07:41

Hmm, well, then this will need a bit more work

pesterhazy21:07:11

I mean we'll need to update re-natal accordingly

pesterhazy21:07:39

I still don't understand fully how "npm-deps" works

pesterhazy21:07:55

does it generate js/require calls?

juhoteperi21:07:12

So there are several different things

pesterhazy21:07:13

you just said that

juhoteperi21:07:26

:npm-deps is just about automatically installing npm packages when running Cljs

pesterhazy21:07:55

installing as in "the cljs compiler actually shells out to npm"?

pesterhazy21:07:09

wow that's wild

juhoteperi21:07:10

Separate to that, Cljs compiler will index node_modules and convert required CommonJS modules to Closure modules

juhoteperi21:07:25

But :npm-deps doesn't need to be used

juhoteperi21:07:41

It is enough that packages are present in node_modules dir

pesterhazy21:07:21

do you propose we do that in cljsrn?

pesterhazy21:07:37

i.e. convert react to a closure module?

juhoteperi21:07:53

If there is (:require [react]), the logic goes something like 1. Is there Cljs namespace or Closure Module by the name 2. Is there Node package in Node modules 3. Is there foreign-lib which provides this name

juhoteperi21:07:06

I don't know how React-native works

juhoteperi21:07:37

There are probably people on #cljs-dev who know more about this

pesterhazy21:07:06

it'd be a good idea to give it a try but i'm not sure i'll have time to do that soon

pesterhazy21:07:35

my worry is that other npm dependencies (e.g. react-native, or any kinds of react-native packages) won't find the same React as Reagent

pesterhazy21:07:00

it's a common issue that a JS env includes multiple instances of React, which always causes problems

juhoteperi21:07:24

I'm not sure how Npm handles case where you have direct dep on React and other packages depend on other version, but I think Cljs (and thus Reagent) will use the top-most version (direct dependency)

pesterhazy21:07:50

Anyway there's no use in speculating, someone will have to try to get re-natal working with 0.8.0-SNAPSHOT

pesterhazy21:07:12

This could make things a lot cleaner, so thanks for putting in this work juho

deadghost22:07:23

so I'm trying to setup a simple render counter for a component

deadghost22:07:44

so I'd close over the component with a r/atom

deadghost22:07:57

and then what?

deadghost22:07:17

where should I do the incing?

gadfly36122:07:16

@deadghost untested, but if you wanted to use a component-local ratom, it'd be something like this

(defn counter []
  (let [counter-ratom (reagent/atom {:count 0})]
    (fn []
      (let [count (get @counter-ratom :count)]
        [:div
         [:div (str "The current count is: " count)]
         [:button {:on-click #(swap! counter-ratom update :count inc)}
          "Increment count"]]))))

deadghost22:07:52

@gadfly361 I think the trickier part is where and how to do the inc to get a correct count of re-renders

gadfly36122:07:37

i dont think i understand what you mean

deadghost22:07:09

I have a nav component, every time it re-renders I want count to inc

gadfly36123:07:30

ah ok, i understand now, you literally want to count re-renders. Hopefully someone can help 🙂

noisesmith23:07:57

@deadghost search here for :reagent-render - I think that's your ticket https://github.com/Day8/re-frame/wiki/Creating-Reagent-Components

noisesmith23:07:13

oh wait that might be reframe only sorry

deadghost23:07:29

well I'm on re-frame so looking

noisesmith23:07:53

now I think it's pure reagent (that part at least)

gadfly36123:07:49

reagent-render is from vanilla reagent

deadghost23:07:45

right now I'm thinking a form 3 component with a closure plus :component-will-update

mikethompson23:07:29

To count renders ....

(defn counted []
  (let [render-count (reagent/atom 0)]
    (fn []
      (swap! (swap! render-count inc))     ;; <-------  count renders
      [:div
        [:div (str "The current render count is: " @render-count)]])))
* warning parens might not be balanced

mikethompson23:07:37

Ie. each time the render function is called, inc

deadghost23:07:48

You meant one swap! right?

mikethompson23:07:19

The only way a component can get re-rendered is for its renderer to get called

mikethompson23:07:34

So put your inc in there

mikethompson23:07:50

Oh, yeah, one swap

mikethompson23:07:22

okay, this is saner ...

(defn counted []
  (let [render-count (reagent/atom 0)]
    (fn []
      (swap! render-count inc)    ;; <-------  count renders
      [:div (str "The current render count is: " @render-count)])))

deadghost23:07:28

wouldn't a swap! there cause an infinite loop of re-renderings?

mikethompson23:07:10

Yes it would, sorry

mikethompson23:07:13

But, if you are showing the count AND counting, then that's unavoidable

mikethompson23:07:09

(defn show-counter
   [r] 
   [:div "Count is" (str @r)])

(defn counted []
  (let [render-count (reagent/atom 0)]
    (fn []
      (swap! render-count inc)    ;; <-------  count renders
      [show-count render-count])))

mikethompson23:07:52

Now there's no infinite loop

deadghost23:07:00

yeah makes sense, can't count and show in the same component

deadghost23:07:19

well in that case, I suppose render-count can't be component local as it needs to get passed into show-counter

gadfly36123:07:18

It can be component local, but you will just need a reason for the component to update by either passing in another atom that it derefs, or passing in some argument that changes ... something along these lines seems probable since there would be no use-case to count a components renders that never rerendered

gadfly36123:07:12

@deadghost this is a working example of counting the rerenders from two sources based on what @mikethompson showed above

(defonce app-state
  (reagent/atom {:foo 0
                 :bar 0}))

(defn show-count [r]
   [:div "Count is" (str @r)])

(defn counted [ratom]
  (let [render-count (reagent/atom 0)]
    (fn [ratom]
      (let [{:keys [foo
                    bar]} @ratom]
      (swap! render-count inc)
      [:div
       [:div "foo count is: " foo]
       [:div "bar count is: " bar]
       [show-count render-count]]))))

(defn page [ratom]
    [:div
     [:button {:on-click #(swap! ratom update :foo inc)}
      "Increment foo"]
     [:button {:on-click #(swap! ratom update :bar inc)}
      "Increment bar"]
     [counted ratom]
     ])


(defn reload []
  (reagent/render [page app-state]
                  (.getElementById js/document "app")))

(defn ^:export main []
  (reload))

deadghost23:07:33

seems to be working

deadghost23:07:02

the bit of indirection bothers me as I don't understand it

deadghost23:07:45

why does it not go into an infinite loop when show-count is turned into a separate component and passed the atom?

gadfly36123:07:29

because of where the deref happens

gadfly36123:07:38

if the deref of render-count belonged to counted then everytime render-count was swapped, it would tigger a rerender of counted, which would then tigger another increment, etc. However, with the deref of render-count in a child component, then counted is not rerendered when render-count is incremented

deadghost23:07:09

so what you're saying is, it's possible for child components to be re-rendered more times than the parent

deadghost23:07:50

huh ok, I understood that part incorrectly then

gadfly36123:07:00

perhaps i am misspeaking tho, but the deref in a child component is the secret sauce as i understand it. please correct if i am wrong @mikethompson