Hacker News new | past | comments | ask | show | jobs | submit login

I'd like to add improper use of useEffect to this list. useEffect is an escape hatch but I often see devs using it as some kind of ad-hoc event driven system approach. useEffect divorces the outcome of an action from its invocation which is problematic. It can lead to some tricky state management scenarios really quickly.

And as a side note: can we *please* stop shitting on every React post that appears on HN? All this hater energy is really starting to be a drag.




I would say useEffect is not an escape hatch, it's a fundamental building block of the hooks paradigm. You need some way to run impure side effects after your component has been rendered, and `useEffect` is how you register those effects.

The most basic example is making an external request based on props: can't do that in the render loop because it has to be pure, since React may run it multiple times before committing to the DOM. `useEffect` is the only way you can get a guarantee that it will only be run once after rendering, and only when its dependencies change.


This is definitely true, but I also think it's overused in typical UI components. I often see patterns where some user event triggers a state change, which triggers a useEffect hook. In most cases, you could instead have your event handler directly trigger your side-effect code. Adding useEffect into the mix is a huge point of failure because, well, it sucks. Until you get that chain of deps just right it's not going to run when you think it is, or it's going to run with some stale values.


> I would say useEffect is not an escape hatch, it's a fundamental building block of the hooks paradigm

The react docs literally call it an escape hatch [0]. First sentence.

[0]https://beta.reactjs.org/learn/you-might-not-need-an-effect


That's funny, they're such a necessary tool for making anything more than simple UI's that I wouldn't consider that an escape hatch. Kind of like saying that screwdrivers are an "escape hatch" for hammers.


Yep, and escape hatch is actually a great descriptor.

useEffect let’s you safely communicate with the world outside react (ie cause side effects). It’s the only reliable way to …escape.


These are the new Beta docs.

The original docs made no such claim.

Which brings us back to the biggest problem with useEffect. Even the react devs don’t seem to have a clue how it should actually used, until only recently, despite it being among the 2 most used hooks ever since hooks were introduced.


What about any of that invalidates my assertion that useEffect is not the tool to reach for most of the time?

The original docs are outdated

The abstraction itself is ripe for misuse

Tons of devs use it when they shouldn’t.

Insert shrug emoji here.


To make it concrete, I think they're saying, if state "rows" depends on state "filter" and "sortOrder", people write `useEffect(() =>setRows(...), [filter, sortOrder])`.

Ie writing reactive code but relying on the React render loop to trigger reactions. The problem is that you can no longer step through your code in full and as you say, your code now mandates flushes to the DOM that are unnecessary (mid computation).

It's better to setRows at explicit places or learn and use a library like rxjs if you really need to encapsulate that complexity.


Right. It's how mutations are done.

But there is a lot of mutable code that could be immutable, and `useEffect` is how that happens in React.


Sorry, what do you mean by mutable vs immutable code? I don't think it necessarily has to do with mutability, because you can "mutate state" in regular callbacks without useEffect. The only way to get true mutability in React is via refs, all other state is immutable.


I mean pure vs impure.

> you can "mutate state" in regular callbacks without useEffect

Yes, via `useState`. That too gets overused. (Note: useEffect and useState are 100% necessary, but also easily overused.)

useEffect for when you want to manipulate state outside the component. I.e. change the document title, exchange data with an HTTP server, store data in localStorage.


Also: even with hooks, the component is still a pure function in a less low-level kind of way.

The hooks yield effect descriptions, to be carried out later, on a side channel. So the component is still a pure map of the inputs to the outputs, just that the outputs are not just comprised of the javascript function's return value, but also the effects on the side channel.


Any function is a pure function if your consider the entire program state as your input and output.


You really don't need to give hooks access to some global variables (or all of them). You can, of course. But you would actually do that in a useEffect or use some setState in a callback somewhere.

It's a cheap shot to complain that Javascript isn't a purely functional language... React can be used in a pretty pure way and if you don't you'll don't get the full benefit.


I see you've discovered the IO monad in Haskell.


That’s not pure anymore for any practical definition.

When you get different answers for the same question, then you’re not calling a function.

When you can only get the same result by recreating the same internal state through external manipulation, you’re dealing with side effectful, imperative code.

Hooks are ergonomic and easy to reason about and that’s great. But they turn functions into objects.


What you’re missing here is that the hooks form part of the input to the hook/render function. There’s a reason they can’t be conditional (though with a compiler theoretically they could be).

The whole idea of the useState hook, is that it’s _not_ internal state to the hook. That state isn’t stored on the stack of the hook function, but against the component.


I understand that, but your component is now an object that can be mutated via events and it tracks internal state via calls to useState.

You cannot call the component with the same arguments getting the same results anymore AKA it’s not pure. You indirectly mutate it via event handlers, which are effectively methods.


no, it can't be mutated by events. The hooks only react to inputs in the side channel and only during execution.

If you attach an event handler, that "attaching" is an effect and executed outside the rendering. If the handler changes some state of the context, a rerender is triggered.


We're either talking about two different things or you are missing the forest in the trees.

The value proposition of FP doesn't come from whether you feel like you're doing FP during implementation. It can only be assessed from the perspective of the caller. The caller doesn't care about the philosophical differences of how you achieve internal mutation or the implementation thereof.

They only care about whether you are returning the same result when they give you the same arguments. That's how you satisfy a function interface.

A component that keeps track of internal state with useState and modifies it via event handlers does not satisfy a functional interface, because given the same props, it may or may not return the same docoument fragment.

One cannot pass in the same state, nor can one see the state from the return value. The signature of a component that uses useState and event handlers to modify it, _hides_ internal state from the caller. A component like that doesn't satisfy a functional interface but does in fact hide an object interface via local retention and message passing.

Vice versa, a component that bangs on an internal variable in order to derive/calculate values that it returns, can satisfy a functional interface from the caller's perspective. Similarly you can use useState and useEffect to implement a functional interface. It all comes down to whether you are giving the same answer for the same question, which is typically not what you're doing with hooks.

Note that I'm not making a value judgement about whether a component is a function or an object (via hooks). If you need an object, then write an object. But be aware of the tradeoffs of writing a function vs an object and be upfront about it to your caller.


It's really just a question of definition and Perspective. From the perspective of the JIT or Assembly, there's no such thing as FP. So you got to make abstractions.

And a very helpful abstractions is to say that the context that you are saying is impure is just another parameter and another part of the return value. Because that's how React is treating context, and that's why you can treat React components as functionally pure if you respect the rules of hooks.

FP doesn't demand the parameters stay the same. It also allows for outputs of a function to get put back in as a parameter in a subsequent invocation, which is what happens to the context. If you insist on seeing it another way, I can't stop you, it's just more complicated and I don't see how that's helping.


> FP doesn't demand the parameters stay the same. It also allows for outputs of a function to get put back in as a parameter in a subsequent invocation, which is what happens to the context.

My point is that _you as the caller_ are _not_ doing this when calling a stateful component Foo. Foo mutates between events and state is entirely encapsulated, so you are getting different results from the same question. The caller doesn't care about the internal execution model of Foo and React. A functional interface is _only_ satisfied if the function is referentially transparent. Which in this case it is not.

If all you can do is send messages and observe behavior from outside. It behaves exactly like an Object and not like a Function from the perspective of the caller.

---

You are arguing in terms of the implementer of a stateful component. You are thinking in terms of how React/useState behaves from inside your component and further up the call stack. And in this context I agree. You can think of useState as part of your signature so to speak. In fact the hooks rules prevent you from doing things that violate this mental model.

But that's not what I'm talking about. I'm talking about the practical, real world perspective of a caller and whether they observe functional or object oriented behavior from your component.


> hooks form part of the input to the hook/render function.

They are input, but they are not input parameters.

A function is pure if it returns same result for same input parameters. Hooks make it possible to return different result for same input parameters, therefore making the function impure.


Again, no. Functional components are not defined by just their "javascript function" but rather by the contract with the rest of the rendering system.

And that's why the context manipulated by hooks looks like a sideeffect, and it often does describe sideeffects, but it doesn't. That context is both an input and an output, only the rendering logic in React decides what to do with that output. Without the rendering logic deciding to execute those sideeffect, the context isn't actually changed at all. Which is why you usually don't need to care about when or how often the component is executed.


From https://en.wikipedia.org/wiki/Pure_function: "the function return values are identical for identical arguments".

The whole point of pure function is that the caller can understand its dependencies without looking at its implementation, because all dependencies are listed as parameters.

You can redefine what "pure" means if you wish, but that pretty much defeats the purpose.


The last statement isn't true unless you mean "mutability" in just the DOM elements.

All sorts of things can carry state and be mutable in a React application. Even the properties of a component are actually mutable, and it sometimes happens that people pass around arrays or objects and mutate them when they shouldn't.


Well, javascript is mutable so technically nothing is immutable. But I'm referring to working with React's hooks, where all props and state should be treated as immutable except for refs.


Refs are just as immutable in that perspective. And no, you're not supposed to mutate ref.current.anything outside useEffect or a callback or similar...


Of course you can! That's the whole point of refs, and why you can't use them as a dependency to callbacks or effects. They're totally mutable, which makes them very tricky to deal with. I mean, I wouldn't recommend updating them outside of effects/callbacks, but it's valid. For example: you can make a ref to count the number of times React calls your render loop by updating it in the body.


> And as a side note: can we please stop shitting on every React post that appears on HN? All this hater energy is really starting to be a drag.

Kind of ironic when this post kicked off a long argumentative thread shining a light on the exact things people don't like about React... "escape hatch", "impure side effects", "useEffect... huge point of failure... it sucks", arguments whether hooks and refs are immutable or not... and these comments are coming from people who claim to like React!


The purest form of function programming cannot have side effects. But to do anything useful you need them.

So like many other functional programming langs/libs/frameworks react had a controlled means to run side effects.

That’s the escape hatch of useEffect. It’s not a bad thing. It’s thematically same as the unsafe block in Rust. It allows you to reach out of the main paradigm, when required.


React was the thing that convinced me that you can't have a non-pure reactive system.

React is complex because views aren't pure; slow because it has to rerender things that didn't change, because it doesn't actually know what changed, because views are impure; hard to program because impure views create bugs that even that extra complexity can't fix; and has all those escape hatches to change execution time and memoize things because the execution time actually has semantic value.

And that's react, that incredibly well designed thing with incredibly knowledgeable authors, that made a huge effort to think about every problem.

Non-pure reactive systems just do not work.


I fully agree that impure views are a path to madness, but you don’t have to abandon React to have pure views.

React components can, and I would argue in vast majority of cases should, be pure functions. You just need to displace state management outside of React.

This removes the need for class components and hooks and returns React to its roots as a view library. Most complications in React seem to stem from its desire to also be a state library.

Case in point: we used MobX with React to build a UI for an entire document management system (essentially a file system with versioning, security and workflows) - I don’t think we have more than a handful of impure React components in the entire codebase.


Interestingly, the optimal place I see to keep state is inside the reactive system. You just have to ensure that every single non-pure action is notated as an event.

You can even have a lot of pure state interdependence. What React doesn't even try (because yes, it's a view library).

Anyway, one can certainly keep all views pure. It's a bit hard in Javascript where a lot of things are hidden, but it's perfectly doable. It's even the recommended way to use React. But that doesn't save React from being way larger, slower, and harder to use than it could otherwise be.


We've had great results from our custom built non-pure reactive system (which predates React by a couple of years).

The idea is to have an Observable object that can store arbitrary trees of data (each itself an Observable). UI drawing is generally split into a function call for each DOM element that has children. These functions are executed such that they track the use of observables and will be rerun when they change.

I've more recently, after having suffered React and friends, started creating an Open Source implementation of the pattern we used. Currently, it's mostly still lacking in documentation and marketing, and that's unlikely to change if I'm honest. But this should give you an impression if you're interested:

https://github.com/vanviegen/aberdeen/blob/master/examples/t...

Curious to know your thoughts about this being an effective non-pure reactive system.


> because it doesn't actually know what changed, because views are impure

Because in React, the smallest unit is the component.

In Solid the smallest unit is the whatever reactive value you use, and only that changes. Components are there just to organize your code and do the initial render.


Quite similar to build systems. Same arguments lead to Bazel...


I know. A bunch of dudes talking out their ass about me calling useEffect an escape hatch when it’s literally called that IN THE DOCS. HN never disappoints.


I don't have a dog in this fight but I think that people here don't care so much about what is said, more about what is shown.

E.g. an outspokenly pro-climate politician who passed the 'frack through orphanages with baby seals bill' will not be taken seriously because actions don't match reality.

IIRC useEffect was not originally called an escape hatch and that terminology was pegged on once they realised that a fundamental hook was actually a 30mm footgun.

To make it worse they haven't provided a clear way to deal with the problem instead relying on 3rd party packages


You’re conflating clarifying intent with outright deception. The react team is trying to clarify how useEffect is intended to be used because it’s so frequently misused. The fact that it’s a possibly a poorly designed abstraction that is ripe for misuse is not really relevant to the discussion imo.

Don’t take my word for it though. Dan Abramov himself has said pretty much what I’m saying (you’d have to find it on Twitter). Ken C Dodds shared a talk (not run by him) called “Goodbye UseEffect”. Look this stuff up if you haven’t. It’s out there. There are often better ways than useEffect. And when those don’t work you can always use the esc… well you get the idea.

EDIT: spelling


https://beta.reactjs.org/learn/you-might-not-need-an-effect

The beta react docs have a number of great examples of this!


> can we please stop shitting on .. [disfavoured tech of the month/year/decade]

cf. "can be please stop hyping .. [favoured tech of the month/year/decade]"

Talk about pissing into the wind.


Calling useEffect an escape hatch is as much of a recent fad as shitting on react. How can it be an escape hatch, when it is the only api provided by react to perform side effects during the rendering cycle, which do not themselves pertain to rendering? Calling it an escape hatch is the same as calling componentDidMount or componentDidUpdate methods of the class-based api escape hatches.


I mean the react docs literally call it an escape hatch[0]. First sentence.

[0]https://beta.reactjs.org/learn/you-might-not-need-an-effect


That’s bad wording though. A fundamental building block shouldn’t be called an escape hatch.


Ah yes. It’s the docs that are wrong.


I understand why it’s worded that way in this specific article. But it’s still unfortunate to call it an escape hatch.

This term is typically used for ways of breaking the rules of a framework. With useEffect you’re still very much in React world and need to follow the rules to achieve much of anything with it.

The norm would be not to use an escape hatch. But I doubt there are many React applications that aren’t using useEffect.


In useEffect your code runs at a specific point, where it’s safe to bridge react to the outside world. It’s not about breaking the rules, as much as you have the ability to cause and react to side effects (eg a fetch promise) and inform react about the result (with a setState).


That’s much better explanation and more precise wording than the linked article did. I like the notion of “safe bridge”.


The last "solution" even commits this mistake. Blind leading the blind.


Using functional components would be a mistake in general.


Why? Do class based components avoid some downsides?


I’m not against React but it sure seems like it’s more complicated than vanilla JS. The hater energy might be a clue knocking at the door, trying to quietly inform you of something you don’t want to hear.


I’m not against indoor plumbing but it sure seems like it’s more complicated than a shallow hole dug in the ground.


The people who work professionally with react all day are well aware of React’s problems. But I’m a front end developer. I’m not about to change careers and become a database administrator because managing state in React is unintuitive. (And while there are other frontend frameworks and solutions, they have their own problems, and it’s more difficult to find a job using them.)


Lol "quietly". React has been around for over ten years with one of the largest grassroots ecosystems built around it. Maybe that should be a clue that it has something to offer?

I think HN is averse to it because it's blamed for "how complicated the web has become". If only we would all settle for simple static websites!


It’s not binary. There can be reasons to use it and reasons not to.


Maybe, but it’s more likely to be boring noise from immature script kiddies who only want to use the newest tech because it’s cool.


I think our industry should accept that React sucks in every engineering metric. And then we move on and no one is going to complain about it.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: