Fork me on GitHub

re-frame is asynchronous, right? when i do a dispatch, do all the event handlers are run asynchronously?

Raymond Ko20:04:29

Hello all, is there a checklist or guide for figuring out why a subscription is not updating (has the wrong data?)

Raymond Ko20:04:30

I have an effect which eventually calls an event does a (assoc-in db ks big-complex-map)

Raymond Ko20:04:29

And a subscription (get-in db ks) which gets this value. I have tried print statements and re-frame-10x, and they are getting different values.


I’m not sure when 10x updates its values, but there’s a common gotcha whenever a re-frame view fails to update when it should


The problem is that the underlying React identifies DOM elements by a key, so if you have, say, a bunch of [:li ...] elements inside a [:ul], sometimes when the list of :li’s changes, React keeps displaying the original [:ul] (with the original, unchanged :li’s)


The solution I’ve used is to put a key on the parent element, and make sure the text of the key changes whenever the child elements change.


Recently I had an Edit User form, and I wanted certain fields to be disabled if the username was invalid. I had a username-valid? subscription, and it was updating when it was supposed to, but it was never disabling the fields when username-valid? changed to false. So I did this:

^{:key (str "user-fields-" (if username-valid? "valid" "disabled"))}
        ... ]


That was enough to get React to notice that the contents of the div had changed.

Raymond Ko20:04:19

In my case, I am changing db in an event, but my Form 1 component with subscribe isn't picking up the changes and re-rendering.


Do you have a gist or anything you can share?


And is your Form 1 component inside something else?


Often the Form 1 component is fine, but the problem is that React is holding onto the parent that contains the Form 1 component.

Raymond Ko20:04:00

Unfortunately, no. This is an internal company project. I have been using re-frame for like a month, so I am just baffled as I have dozen of events, subscriptions, and effects, and this is first time this is happening.

Raymond Ko20:04:19

But even for nested Form 1, won't it re-render if it detects that the subscription value has changed?

Raymond Ko20:04:26

Or am I misunderstanding how this works?


It’s definitely a perplexing quirk. You’re right about how it’s supposed to work, and most of the time that’s how it does work. It’s just this odd little wart that crops up now and then.


I think what’s happening is that it has already rendered the parent element, so it just re-uses that DOM node, since it has the same React key. If you can get the key to change on the parent, that forces React to re-consider the state of the children as well. It’s supposed to work without you needing to do that, and most of the time it does. When it doesn’t, you can give it a kick in the pants by forcing the React key to change.


And I should say, I can’t be sure that this is the problem you’re facing now, but I’ve had so many times when it happened to me, and I’ve heard of it happening to others, and we’re all like “What the heck?“. And then the {:key ...} trick fixes it, so I always mention it.


Also I’ve seen a reference somewhere in the React documentation that mentions forcing the key to change to get React to pick up on changes to child elements, so I’m assuming it happens in plain old JS React too.

Raymond Ko20:04:50

Okay, thank you for the help. I need to figure out how to try that. Like right now my parent is a top level after the root div, and it doesn't even have a key. I am adding (js/console.log) statements, and then dispatching effects in the REPL which clearly call events, which clearly change the db (I see this is re-frame-10x). But this top-level function stubbornly does not re-render, leading to confusion. I have done before with other views and am struggling to figure out what is going on.


Ok, try something like this. Assuming your subscription is called my-sub, right before the parent div, add {:key (str "my-sup-parent-" @(re-frame/subscribe [:my-sub])}


Re-frame subscriptions are cached, so it’s ok to do an immediate de-reference on the call to subscribe.

Raymond Ko20:04:10

I finally figured it out, and it turns out to be an embarrassing mistake on my part. My code only fetches data set A one once on startup and I was using the REPL to re-fetch and apply transformations.

Raymond Ko20:04:17

It turns out I was fetching data set B...

Raymond Ko20:04:09

So of course the branch in db for data set A is not going to change...

Raymond Ko20:04:44

Anyways, thank you very much for your help. I'll be sure to keep that trick in mind since I am also doing form input.