helix

https://github.com/Lokeh/helix
Derek Passen 2020-02-21T15:59:27.289Z

There should be no difference between (hx.hooks/useEffect (fn [] .. ) [foo]) and (helix.hooks/use-effect [foo] (fn [] .. ) right? Other than the arg order change and support for :auto-dep :once and :always?

lilactown 2020-02-21T16:01:43.289400Z

That's correct. Are you seeing a difference?

Derek Passen 2020-02-21T16:02:24.289600Z

Yes, and I’m not sure why

Derek Passen 2020-02-21T16:03:00.290400Z

I put in some debug console.logs and saw some behavior where auto-deps was running on every render and the one depending on a single binding was never running

lilactown 2020-02-21T16:03:01.290500Z

Oh wait

lilactown 2020-02-21T16:03:26.291300Z

Helix's use-effect expects you to write the body without a wrapping fn

Derek Passen 2020-02-21T16:03:58.292100Z

gotcha, let me give it a shot

Derek Passen 2020-02-21T16:07:20.292400Z

that was it

Derek Passen 2020-02-21T16:07:26.292600Z

thanks for your help

lilactown 2020-02-21T16:07:35.292800Z

Sure thing

lilactown 2020-02-21T16:08:17.293600Z

I'm honestly contemplating removing a lot of the sugar for hooks like use-effect

lilactown 2020-02-21T16:09:02.295Z

Not needing to wrap it in a fn is nice, but it's surprising given that no 3rd party hooks work that way, and I can't extend the behavior to them

Derek Passen 2020-02-21T16:09:45.295600Z

We were often doing (hx.hooks/useEffect (fn-that-returns-a-0-arity-fn) [deps])

lilactown 2020-02-21T16:13:37.295800Z

hm interesting

2020-02-21T16:32:10.296300Z

Is there more sugar to be removed than the implicit fn?

lilactown 2020-02-21T16:33:53.296900Z

I’ve also been thinking of deprecating :auto-deps in favor of something better, but that’s a long ways off

lilactown 2020-02-21T16:34:31.297600Z

in practice I haven’t actually found :auto-deps that useful, although there are certain cases where I want that behavior

lilactown 2020-02-21T16:38:56.301900Z

my future plan is to introduce a defhook form which allows you to annotate arguments that are dependencies:

(defhook use-thing
  [^:deps deps f]
  ,,, do things with deps vector and f)
which would allow defnc to detect and warn if you haven’t filled in the deps vector completely:
(use-thing
 []
 #(foo bar)) ;; => Warning! Deps array missing `foo` and `bar`

(use-thing
  ^{:deps/ignore [foo]} [bar]
  #(foo bar)) ;; => Fine
and use a macro to auto-fill them for you:
(auto-deps
 (use-thing
  #(foo bar baz ... xyz)))

👍 1
2020-02-21T16:49:38.303600Z

I don't quite see the need for :auto-deps or auto-deps. To me it seems quite a bit of mental overhead for very little gain.

2020-02-21T16:52:42.307400Z

It's important to quickly understand when a given effect is run by looking at the explicit deps. With auto-deps I'd have to carefully read the whole fn to understand when it's run.

lilactown 2020-02-21T16:54:09.308800Z

I agree completely re: use-effect. the case I do see it as helpful is with optimizations like

(defnc my-component
  [{:keys [prop1 prop2 ,,, propN]}]
  (let [{:keys [context1 context2 ,,, contextN]} (use-context some-global)]
    ;; here we wrap the React Elements in use-memo to bail out of
    ;; rendering if the pieces of context we're using haven't changed
    (use-memo
     :auto-deps
     (d/div ,,,))))

2020-02-21T16:55:54.309800Z

That's a good point.

lilactown 2020-02-21T16:56:03.310Z

my dream is to boil that down to

^:memo (d/div ,,,)

lilactown 2020-02-21T16:56:36.310600Z

but I agree that there’s a semantic difference between things that do side-effects, which I really want to be explicit, and things that optimize, like use-memo and use-callback

lilactown 2020-02-21T16:57:32.311500Z

finding a way to easily inspect and troubleshoot the forms that the syntax sugar emits is also something I’m thinking about

2020-02-21T17:07:16.317Z

I have written quite a bit of reagent and rum, and have only experimented with hx/helix (and hooks in general) so far. Maybe the biggest surprise for me was how often I need to use memos to make sure I don't just dumbly re-render too much. I don't buy the React idea that virtual rendering is so cheap that I don't have to think about it. It surely matters when there's a large list on the screen, for example. Also I don't want to use my users' precious mobile battery to re-render the same stuff all over again that they already have on their screen. So I like your idea of making these optimizations as straightforward as possible.

lilactown 2020-02-21T17:22:20.320600Z

yep, I agree with you. performance is critical. reagent/rum and other libs that coordinate state outside of the component tree have a leg up w.r.t. performance in certain conditions. but my belief is that Concurrent Mode will be a big enough win in general that it will make it so that we only want (or need) to use those external state containers in very specific circumstances

Derek Passen 2020-02-21T19:02:08.321900Z

factory functions should be similar in performance to hx. is that correct?

2020-02-21T19:02:13.322Z

Yeah, I see it the same way. Have you published any (experimental) examples of data fetching with Suspense in helix?

lilactown 2020-02-21T19:14:55.322600Z

I haven’t published anything yet, but I have been working on an example hacker news reader that uses suspense for data fetching

lilactown 2020-02-21T19:15:26.323200Z

hoping I can use it to show some of the cool new features React has in it’s experimental branch, but there are some rough edges

lilactown 2020-02-21T19:16:28.324300Z

yes

lilactown 2020-02-21T19:16:53.325200Z

mainly around the rest of the ecosystem, tbh. things like react-router and react-spring don’t operate as well as one would hope in Concurrent Mode. but I think a lot of that will be ameliorate as adoption rises

Derek Passen 2020-02-21T19:17:38.325500Z

Thank you

2020-02-21T19:18:26.326300Z

Okay! Sounds interesting. I hope you'll decide to publish it sometime soon. 🙂

2020-02-21T19:20:35.326900Z

Is there a specific reason to use react-router instead of something that's implemented in cljs on top of helix?

lilactown 2020-02-21T19:22:12.327100Z

I thought it would be easier than writing my own 😛

lilactown 2020-02-21T19:23:07.327600Z

there’s a lot that goes into routing (managing history, etc.) that I didn’t want to have to write

2020-02-21T19:28:04.330400Z

Did you consider secretary, bidi or reitit? They are easy to integrate to a reagent project, but would there be problems with helix (because of hooks)?

lilactown 2020-02-21T19:29:34.331300Z

I have briefly started integrating reitit after running into issues with react-router, but I think it’s going to run into the same problems as react-router if I do it naively

lilactown 2020-02-21T19:30:52.332500Z

the problem in concurrent mode is that state changes that exist outside of the component tree (like, say, the window’s path) doesn’t play as nicely with things like Suspense

2020-02-21T19:32:04.333700Z

Ah, I see.

lilactown 2020-02-21T19:34:02.334900Z

like ideally, when a user clicks on a link to go to another view that needs some data, Suspense would intelligently determine whether to show interstitial states • if the data is already there, transition immediately • if the data isn’t there yet, but resolves very quickly, wait until it resolves before transition to the view and show local loading spinner on the link/button • if the data isn’t there yet, but is taking awhile, transition to the view and suspend with a full-page loading animation until resolved

lilactown 2020-02-21T19:34:18.335200Z

this is what the useTransition hook is for managing

lilactown 2020-02-21T19:35:46.336900Z

React will do all that for you if you use use-state, but if you are changing the state synchronously (by updating the window URL, for instance) and then reading that state from outside the tree to dispatch the view change, then React can’t do it’s smartness

2020-02-21T19:39:53.338200Z

If one would addEventListener('popstate', ...) in useEffect, and change the state from there, would it then play nicely with Suspense?

lilactown 2020-02-21T19:41:03.338900Z

AFAIU in this case no, in order to get all the nice behavior you need to fire the set-state call from inside the useTransition callback

lilactown 2020-02-21T19:41:35.339200Z

i might not understand it well enough yet tho

2020-02-21T19:41:47.339500Z

Okay. I guess the Concurrent Mode is quite a can of worms.

2020-02-21T19:42:39.340300Z

Do you know if they use it in production in Facebook? Any info available how they have solved routing there?

lilactown 2020-02-21T19:42:49.340500Z

they do use it in prod AFAIK

lilactown 2020-02-21T19:42:54.340700Z

I’m not sure

lilactown 2020-02-21T19:43:43.341600Z

I think what might work is managing the router state inside of React, and pushing/popping the history state as a side effect

lilactown 2020-02-21T19:43:51.341900Z

but that might be a whole nother can of worms

2020-02-21T19:52:37.343300Z

Thank you for a very interesting conversation. Gotta go help my child now. I really appreciate your work and help, and hope to write great UIs with helix soon. With or without Concurrent Mode. 🙂

lilactown 2020-02-21T19:52:58.343500Z

cheers! best of luck 😄