Hacker News new | ask | show | jobs
by inglor 1551 days ago
I work in an infra team in a large codebase for Microsoft. We are the people who do the sort of bug-analysis to figure out what technologies work well and what needs improvements.

Hooks are a constant area of struggle and people make tons of mistakes (forgetting to cleanup a useEffect, useState closures, and needless useCallback are the top 3) with it.

I dare say that if we didn't have MobX things would have been much much worse.

The annoying bit is that other frameworks like Vue or Solid have MobX like Reactivity baked in which makes things much much simpler.

7 comments

For me it seems React has made some unfortunate choices in an attempt to go FP in a language that does not afford it very well. Instead of embracing JS and working in alignment with its limitations/offerings, the React team has introduced elaborate workarounds that gives a whiff of FP but introduces indirection and verbosity and does not solve basic problems of state (i.e. the example from the blog post with classes vs. closures).

I prefer frameworks like e.g. Svelte that works with what JS affords. If you want to go full FP Elm or ClojureScript will give you more value for your money than React imo.

FP is a wide spectrum and JS can do some FP well. I'd argue the issue with what React is attempting with hooks is not that it is FP, but it is an attempt at particular "higher-kinded" monads when JS today mostly just has basic support for simple kinded monads (Promises/thenables and async/await syntax). The types of monads that hooks want to be are advanced and require a bunch of extensions even in "big FP" languages like Haskell. (In theory, if the biggest concern was no do-notation in JS, you could approximate it with async/await syntax. The mental model of async/await might not make sense to what you were doing with it, but the dualism with do-notation should be valid. What Hooks are trying to be needs things like GADTs and other extensions in Haskell from my understanding.)

To me, I still think Hooks are useful for making that big swing attempt in JS despite needing so many crutches like hard education spikes in the learning curve and so many linters, but the problem with Hooks is definitely not that they are "FP" but that they are advanced FP that even FP still hasn't figured out all the bugs.

Not sure what you mean by higher kinded monads but hooks are kind of like dumbed down Arrows.

You'll see some similarities with the haskell libraries auto and netwire.

I'm not a fan of the pure JS implementation the react team did, I wish they went the compiler way like solidjs did. It results in an API that is close enough to react and works faster and without gotchas (or hooks rules).

> Not sure what you mean by higher kinded monads

The FP term of art I was forgetting yesterday was: algebraic effects. Hooks as a whole are trying to model algebraic effects. Algebraic effects are still raw and new in a lot of FP languages. They are hard to reason with even in research languages designed entirely for testing algebraic effects work. The one time I saw an example of trying to model algebraic effects in Haskell it used a half-dozen or more GHC extensions including GADTs on top of monads. (GADTs applied to normal types create higher-kinded types. "Higher kinded monads" was a bit metaphorical, but not the worst description I could have come up with for how algebraic effects looked to me how I saw them modeled in Haskell that one time months ago.)

I wish what React was trying to do was just dumbed down Arrows or Observables. From what I understand of how things like React Concurrency and React Suspense are built to work they are doing a lot more reasoning under the hood about the hook effects than just the raw arrows would imply. I think it is an interesting big swing. I also realize that using ideas from way out in Research Land in a non-type-safe language is a huge risk and liable to create a lot of footguns (as it has).

> I'm not a fan of the pure JS implementation the react team did, I wish they went the compiler way like solidjs did. It results in an API that is close enough to react and works faster and without gotchas (or hooks rules).

I have mixed feelings on this. Pure JS has some advantages. While hooks as they exist offer some footguns, they also allow for some flexibility when you need it (sometimes, very rarely, you do need a closure around a local variable that is not intentionally in the dependency graph). Admittedly right now a lot more people are likely to succumb to footguns than need the flexibility. I think some of that is a balancing act that hooks and especially useEffect are very low level "assembly language" in React and the impression I have is that they mostly were never entirely meant to be used to the extent of writing say 100% of one's business logic in these low level hooks and instead were always intended to be lego building blocks in "higher level" hooks. (Related in part to how in React you aren't likely to build entire components' VDOM by hand in JSON notation, you'd probably use JSX or TSX.) Right now the React community maybe isn't using or building enough "higher level" hooks because the low level hooks also seem to have created a decent "good enough" local maxima hill that "current wisdom" says to die on that hill ("just use hooks, you don't need redux") rather than search for a better mountain.

I think the Typescript wrappers in the article here are helpful. I think the article's reminder here is a useful one that just because you could do everything with raw useEffect doesn't mean you have to and that there are a number of good state management and service layer libraries that are still very useful in React post-hooks.

I was curious and indeed confirmed that ember.js/glimmer.js @tracked properties were inspired by MobX.

From /u/wycats "The rule in Octane is: "mark any reactive property as @tracked, and use normal JavaScript for derived properties". That's pretty cool!"

https://news.ycombinator.com/item?id=21848929

+1 for MobX, can't recommend it enough

[also a quick shill for the general-purpose language I'm working on which internalizes that mechanism at the language level :)]

https://www.brandons.me/blog/the-bagel-language

MobX from a quick Google seems to resemble the old Knockout.js

We use Relay to define our state management, trigger global updates across many components, and also query/mutate our data.

Does MobX have a way to do the global state management that Relay does?

I’m curious because after using Relay, I’ve found most other types of state management to feel extremely heavy with boiler plate code.

Thoughts?

One of the great things about MobX is that it works on plain data and plain operations on that data [0], not just the contents of a sanctioned Store, which means it has no opinions about where you put your data. Local objects captured by closures? Cool. Classes? Great. Mutating arguments? Go for it. Global singleton? Have fun.

[0] Technically, it wraps all the different data structures in Proxies etc as soon as they get assigned into an observable structure. But the goal is for you to never have to think about them as anything other than plain data, and that abstraction very rarely leaks.

Ah, that’s so cool! I will have to carve out some time to test this because that sounds really powerful and flexible!
It is! It can be overkill for very simple React apps, but I used it to build a power-user tool a couple years ago that would have been impossible to do with Redux or hooks.
Hooks are never the solution for state on their own. They're more for localized to a single component state.

That's why I brought up Relay (this is not the same as React, but works with React), cause it's what FB uses to stitch together global state between hundreds of different sub-components.

That being said, Relay does NOT have a simple object interactive state ability. It's an obscure "external store" with weird querying and mutating rules that can feel quite difficult to work with at times.

I only wish Mobx's API was terser and more opinionated. Hard to derive a good Mobx pattern from their bloated API (>100 exports)

I've created a wrapper library which is basically a more-opinionated, higher-level version of Mobx if anyone is interested: https://www.npmjs.com/package/r2v

If Solid or Svelte had a way to import React components (for 3rd party code) and a documented migration path for 1st party code I'd be interested in switching.
How does Mobx help your code work better? Does it specifically salve hooks wounds, or just because you don’t need to manually manage subscriptions in a component?

I’ve looked at Mobx and am concerned about mutation. I would like a magically reactive state container that always returns immutable views of the state for local usage. Maybe I should stop worrying and switch everything to Mobx.

Because you don't need to manage subscriptions, there's a whole class of hooks you don't need to write in the first place. Then on top of that, you get extremely good performance. Having to rarely think about hooks and basically never think about performance, for me, free's up tons of cognitive space to focus on the actual implementation of the UI. Its not a perfect system, but I haven't found another I'd rather use.